Tuesday, December 9, 2014

EJB Transaction Demarcation

EJB supports two types of transaction demarcation programmatic or Bean Managed Transaction and declarative or Container Managed Transaction. With BMT the onus is on the developer to manage the transaction while with CMT the responsibility lies with the container to manage the transaction. Both models can be used within the same application although there are certain situations where they are incompitable. If not specified the default for a bean is CMT. Stateful and MBD both support BMT and CMT

Container-Managed Transactions:- In a CMT,  it is the responsibility of the  container  to start, commit and roll back a transaction . Transaction boundaries in declarative transactions are always marked by the start and end of EJB business methods. More precisely, the container starts a JTA transaction before a method is invoked, invokes the method, and, depending on what happened during the method call, either commits or rolls back the managed transaction. All we have to do is tell the container how to manage the transaction by using either annotations or deployment descriptors and informing the transaction manager when the transaction needs to be rolled back. The container assumes that we will be using CMT on all business methods. CMT is achieved mainly with the help of two annotations @TransactionManagement and @TransactionAttribute. 


@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class CMTBeanManager implements CMTBeanManagerRemote{
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Override
public void someMethod() {
try {

}
} catch (Exception e) {
logger.log(Level.SEVERE,"An error ocurred.", e);
context.setRollbackOnly();
}

@TransactionManagement Annotation:-The @TransactionManagement annotation specifies whether CMT or BMT is to be used for a particular bean. In this case, we specified the value TransactionManagementType.CONTAINER—meaning that the container should manage transactions on the bean’s behalf. If we wanted to manage the transaction programmatically instead, we would have  specified TransactionManagementType.BEAN for the TransactionManagement value. Notably,
although you’ve explicitly included the annotation in the example, if we leave it out the container will assume CMT anyway. 

@TransactionAttribute Annotation:-Although the container does most of the heavy lifting in CMT, we still need to tell the container how it should manage transactions. To understand what this means, consider the fact that our someMethod() could be called from another bean with a transaction already in progress. In this case, do we want it to suspend that transaction, join it, or fail? In the case of this method, it’s being called from the web tier and there’s no transaction in progress, so obviously a new transaction should be started. The @TransactionAttribute gives us the control to determine what the container should do in relation to transactions.




 










  



REQUIRED:-REQUIRED is the default and most commonly applicable transaction attribute value.This value specifies that the EJB method must always be invoked within a transaction. If the method is invoked from a nontransactional client, the container will start a transaction before the method is called and finish it when the method completes. On the other hand, if the caller invokes the method from a transactional context, the method will join the existing transaction. In case of transactions propagated from the client, if our method indicates that the transaction should be rolled back, the container will not only roll back the whole transaction but will also throw a javax.transaction.RollbackException back to the client. This lets the client know that the transaction it started has been rolled back by another method.

 REQUIRES_NEW:-The REQUIRES_NEW value indicates that the container must always create a new transaction to invoke the EJB method. If the client already has a transaction, it’s temporarily suspended until your method returns. This means that the success or failure of our new transaction has no effect on the existing client transaction.From the client’s perspective, 
1. Its transaction is paused.
2. The method is invoked.
3. The method either commits or rolls back its own transaction.
4. The client’s transaction is resumed as soon as the method returns.
The REQUIRES_NEW attribute is of limited use in most applications. It should be used insituations where you need a transaction but don’t want a transaction rollback to affecta client and vice versa. Logging is a great example of where this transaction attributecould be used. Even if the parent transaction rolls back, you still want the log message recorded. On the flip side, if creating the logging message fails, you don’t want the operation it was logging to also fail.

SUPPORTS:-The SUPPORTS attribute means that the method is ambivalent to the presence of a transaction. If a transaction has been started, the method will join the existing transaction. If no transaction has been started, then the method will execute without a transaction. The SUPPORTS attribute is typically useful for methods that perform read-only operations such as retrieving a record from a database table.

MANDATORY:-MANDATORY means that the method requires an existing transaction to already be in progress for this method to join. When the container goes to invoke the method, it’ll check to make sure that a transaction is already in progress. If a transaction hasn’t previously been started, an EJBTransactionRequiredException will be thrown. If the method is invoked without a transaction, an exception will be thrown.

NOT_SUPPORTED:-When assigning NOT_SUPPORTED as the transaction attribute, the EJB method can’t be invoked in a transactional context. If a caller with an associated transaction invokes
the method, the container will suspend the transaction, invoke the method, and then resume the transaction when the method returns. This attribute is typically only useful for an MDB supporting a JMS provider in nontransactional, auto-acknowledge mode. In this case, the message is acknowledged as soon as it’s successfully delivered and the MDB has no capability or apparent need to support rolling back message delivery.

NEVER:-In a CMT, NEVER means that the EJB method can never be invoked from a transactional
client. If such an attempt is made, a javax.ejb.EJBException is thrown. This is probably the least-used transaction attribute value. It’s possibly useful if our method is changing a nontransactional resource (such as a text file) and we want to make sure the client knows about the nontransactional nature of the method.

Marking a CMT for rollback:-Sometimes in the course of a transaction it’s determined that the changes need to be rolled back. The rollback could be due to an error, an invalid credit card number, or a result of a business decision. CMT provides the capability for the bean to signal that the transaction must be rolled back. The rollback doesn’t take place immediately—a flag is set, and at the end of the transaction the container will perform the rollback. Let’s see how this is done:
@Resource
private SessionContext context;
public void placeSnagItOrder(Item item, Bidder bidder, CreditCard card) {
try {
} catch (CreditProcessingException ce) {
logger.log(Level.SEVERE,"An error ocurred processing the order.",ce);
context.setRollbackOnly();
}

}
As this snippet shows, calling setRollbackOnly on javax.ejb.EJBContext marks the transaction for rollback when there’s an error processing the credit card. If we didn’t mark the transaction for rollback, then any changes made would actually be committed. This is an important point to remember—it’s our responsibility to tell the container what to do in the event of an application exception.

Bean-managed transactions:-The greatest strength of a CMT is also its greatest weakness. Using a CMT, we are limited to having the transaction boundaries set at the beginning and end of business
methods and relying on the container to determine when a transaction starts, commits, or rolls back. A BMT, on the other hand, allows us to specify these details programmatically, using semantics similar to the JDBC transaction model. But even in this case, the container helps us by actually creating the physical transaction, as well as taking care of a few low-level details. With BMT, we must be much more aware of the underlying JTA transaction API, primarily the javax.transaction.UserTransaction interface that we mentioned earlier. 

 @Stateless(name = "BidManager")
@TransactionManagement(TransactionManagementType.BEAN)
public class BidManagerBean implements BidManager {
@Resource
private UserTransaction userTransaction;
public void placeSnagItOrder(Item item, Bidder bidder, CreditCard card) {
try {
userTransaction.begin();
if(!hasBids(item)) {
creditCardManager.validateCard(card);
creditCardManager.chargeCreditCard(card,item.getInitialPrice());
closeBid(item,bidder,item.getInitialPrice());
}
userTransaction.commit();
} catch (CreditProcessingException ce) {
logger.log(Level.SEVERE,"An error ocurred processing the order.",ce);
context.setRollbackOnly();
} catch (CreditCardSystemException ccse) {
logger.log(Level.SEVERE,"Unable to validate credit card.",ccse);
context.setRollbackOnly();
} catch (Exception e) {
logger.log(Level.SEVERE,"An error ocurred processing the order.",e);
}
}
Scanning the code, we notice that the @TransactionManagement annotation specifies the value TransactionManagementType.BEAN as opposed to TransactionManageMent-Type.CONTAINER, indicating that BMT is used this time. The TransactionAttribute annotation is missing altogether because it’s applicable only for CMT. A User-Transaction, a JTA interface, is injected and used explicitly to begin ,commit or rollback a transaction.

Getting A UserTransaction:-The UserTransaction interface encapsulates the basic functionality provided by the Java EE transaction manager. JTA has a few other interfaces used under different circumstances. Most of the time we will be dealing with UserTransaction. The simplest way of getting a UserTransaction is  injecting it through the @Resource annotation. There are a couple of other ways to do this: using JNDI lookups or through the EJBContext.
JNDI LOOKUPS:- The application server binds the UserTransaction to the JNDI name java:comp/ UserTransaction. We can look it up directly using JNDI with this code:

Context context = new InitialContext();
UserTransaction userTransaction = (UserTransaction)context.lookup("java:comp/UserTransaction");
userTransaction.begin();
// Perform transacted tasks.
userTransaction.commit();

This method is typically used outside of EJBs—for example, if we need to use a transaction in a helper or nonmanaged class in the EJB or web tier where dependency injection isn’t supported. If we find ourselves in this situation, we might want to consider another approach. It’s much better to have that code in an EJB with greater access to abstractions.
EJBCONTEXT:- We can also acquire a UserTransaction by invoking the getUserTransaction method on EJBContext. This approach is useful if we are using a SessionContext or MessageDrivenContext for some other purpose anyway, and a separate injection to get a transaction instance would clutter the code. Note that we can use the getUserTransaction method only if we are using BMT. Calling this in a CMT environment will cause the context to throw an IllegalStateException. The following code shows the getUserTransaction method in action:
@Resource
private SessionContext context;

UserTransaction userTransaction = context.getUserTransaction();
userTransaction.begin();
// Perform transacted tasks…
userTransaction.commit();

It’s important to note that the EJBContext.getRollbackOnly() and setRollbackOnly() methods will throw an IllegalStateException if we call them while in BMT. These methods can only be called while we are in CMT. The UserTransaction interface has a few other useful methods we should take a look at as well. Let’s take a look at the entire interface:

public interface UserTransaction {
public void begin() throws NotSupportedException, SystemException;
public void commit() throws RollbackException, HeuristicMixedException,HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException;
public void rollback() throws IllegalStateException,
SecurityException,SystemException;
public void setRollbackOnly() throws IllegalStateException,
SystemException;
public int getStatus() throws SystemException;
public void setTransactionTimeout(int seconds) throws SystemException;
}
The begin method creates a new low-level transaction behind the scenes and associates it with the current thread. What would happen if we call the begin method twice before calling rollback or commit. Perhaps it’s possible to create a nested transaction using this approach. In reality, the second invocation of begin would throw a NotSupportedException because Java EE doesn’t support nested transactions. The commit and rollback methods, on the other hand, remove the transaction attached to the current thread by the begin method. Whereas commit sends a “success” signal to the underlying transaction manager, rollback abandons the current transaction. The setTransactionTimeout method specifies the time (in seconds) in which a transaction must finish. The default transaction time-out value is set to different values for different application servers. For example, JBoss has a default transaction time-out value of 300 seconds, whereas Oracle Application Server 10g has a default transaction time-out value of 30 seconds. We might want to use this method if you’re using a long running transaction. Typically, it’s better to simply set the application server–wide defaults using vendor-specific interfaces. At this point, we are probably wondering how to set a transaction time-out when using CMT instead. Only containers using either an attribute in the vendor-specific deployment descriptor or vendor-specific annotation support this.In general, BMT should be used sparingly because it’s verbose, complex, and difficult to maintain. 


No comments:

Post a Comment