仅供参考!TransactionManager.java package org.mockejb; import java.io.Serializable; import java.lang.reflect.Method; import javax.naming.*; import javax.transaction.*; import javax.ejb.*; import org.apache.commons.logging.*; import org.mockejb.interceptor.*; /** * Provides the support for the container-managed transactions * according to EJB spec (chapter 18). * <p> * Note that RequiredNew is not fully supported since <code>TransactionManager</code> * does not know how to suspend transactions. * <p> * Transaction policy must be provided in the invocationContext in the "transactionPolicy" field. * If it is not provided the Supprts policy is used. * @author Alexander Ananiev */ public class TransactionManager implements Interceptor, Serializable { // logger for this class private static Log logger = LogFactory.getLog( TransactionManager.class.getName() );
public final static String USER_TRANSACTION_JNDI="javax.transaction.UserTransaction"; public final static String POLICY_CONTEXT_KEY="transactionPolicy";
/** * "Cached" instance of the UserTransaction */ private static UserTransaction sharedUserTransaction; private TransactionPolicy policy = TransactionPolicy.SUPPORTS; /** * Creates a new instance of the <code>TransactionManager</code> with the * given policy. * @param policy transaction policy */ public TransactionManager( TransactionPolicy policy ){ super(); setPolicy( policy ); }
/** * Creates a new instance of the <code>TransactionManager</code> with the * default (Supports) policy. */ public TransactionManager( ){ super(); } /** * Returns the currently set transaction policy. * @return transaction policy */ public TransactionPolicy getPolicy() { return policy; }
/** * Sets the transaction policy. * @param policy policy to set. */
public void setPolicy( TransactionPolicy policy ) { this.policy = policy; } /** * Begins, commits and rolls back the transaction according to the currently * set policy and EJB spec. */ public void intercept( InvocationContext invocationContext ) throws Exception {
} /* Here we try to follow the guidelines of chapter 18 of EJB spec * with the exception of javax.transaction.TransactionRolledback * Note that ExceptionHandler already provides logging and wrapping services * so we don't need to do it here */ catch( Exception exception ) {
if ( MockContainer.isSystemException( exception )) { // If this transaction was started immediately before this invocation if ( newTran != null && ( newTran.getStatus()==Status.STATUS_ACTIVE || newTran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) ) { newTran.rollback(); log( method, "Rollback because of system exception" ); } } // if it is application exception // we should rollback/commit only if it was new transaction context else { // TODO: according to the spec we must ignore the exceptions from the // rollback if we try to rollback in the catch block of the business // exception. We should re-throw business exception try { commitOrRollback( newTran ); } catch ( RollbackException rollbackEx ) { logger.error( "There has been rollback exception trying to rollback the transaction set for rollback. Ignoring. ", rollbackEx); } }
if ( tran.getStatus()==Status.STATUS_ACTIVE ) { tran.commit(); log( "Committing transaction" ); } else if ( tran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) { tran.rollback(); log( "Rollling back transaction" ); } } }
/** * Performs the actions necessary to handle the transaction policy * according to the spec. * Determines whether the new transaction has to begin for the given method. * @param policy policy of this invoker * @param targetObj bean being called * @param method method being called * @param args parameter values of the method being called * @return true if the new transaction must begin for the given method */ // TODO: We use TransactionRequiredLocalException even for remote EJBs protected boolean handlePolicy( TransactionPolicy policy, Object targetObj, Method method, Object[] args) throws SystemException, NamingException {
throw new TransactionRequiredLocalException( "Attempt to invoke method with Mandatory policy without transaction context"); } } else if ( policy == TransactionPolicy.NEVER ){ UserTransaction tran = getUserTransaction(); if ( tran != null || tran.getStatus()==Status.STATUS_ACTIVE ) { throw new EJBException( "Attempt to invoke method with Never policy inside transaction context"); } } // For Supports we don't need to do anything // NotSupported is the same deal since we can't suspend the transaction
return newTranRequired; }
/** * Returns UserTransaction object. If <code>setUserTransaction()</code> * was called, will return the object that was set by this method. * Otherwise, tries to obtain UserTransaction object from JNDI. * * @return UserTransaction object */ public static UserTransaction getUserTransaction() {
UserTransaction userTransaction = null;
if ( sharedUserTransaction != null ) { userTransaction = sharedUserTransaction; } else { // obtain tran from the JNDI try { Context context = new InitialContext(); userTransaction = (UserTransaction) context.lookup( USER_TRANSACTION_JNDI ); } catch ( NamingException namingEx ){ throw new MockEjbSystemException( "Errors while trying to obtain javax.transaction.UserTransaction from JNDI", namingEx ); } }
return userTransaction; } /** * Sets the shared instance of UserTransaction that will be used by MockEJB. * This is convenient when the remote JNDI is used and the cost of obtaining * UserTransaction object from JNDI every time is too high. TransactionManager tries to * get UserTransaction for every EJB method call. * */ public void setUserTransaction( UserTransaction userTransaction ) { sharedUserTransaction = userTransaction;
/** * Returns true if the given object is of the same type and * it has the same transaction policy. */ public boolean equals( Object obj ){ if ( ! (obj instanceof ClassPatternPointcut) ) return false;
package org.mockejb;
import java.io.Serializable;
import java.lang.reflect.Method;
import javax.naming.*;
import javax.transaction.*;
import javax.ejb.*;
import org.apache.commons.logging.*;
import org.mockejb.interceptor.*;
/**
* Provides the support for the container-managed transactions
* according to EJB spec (chapter 18).
* <p>
* Note that RequiredNew is not fully supported since <code>TransactionManager</code>
* does not know how to suspend transactions.
* <p>
* Transaction policy must be provided in the invocationContext in the "transactionPolicy" field.
* If it is not provided the Supprts policy is used.
* @author Alexander Ananiev
*/
public class TransactionManager implements Interceptor, Serializable { // logger for this class
private static Log logger = LogFactory.getLog( TransactionManager.class.getName() );
String USER_TRANSACTION_JNDI="javax.transaction.UserTransaction"; public final static
String POLICY_CONTEXT_KEY="transactionPolicy";
/**
* "Cached" instance of the UserTransaction
*/
private static UserTransaction sharedUserTransaction; private TransactionPolicy policy = TransactionPolicy.SUPPORTS; /**
* Creates a new instance of the <code>TransactionManager</code> with the
* given policy.
* @param policy transaction policy
*/
public TransactionManager( TransactionPolicy policy ){
super();
setPolicy( policy );
}
/**
* Creates a new instance of the <code>TransactionManager</code> with the
* default (Supports) policy.
*/
public TransactionManager( ){
super();
} /**
* Returns the currently set transaction policy.
* @return transaction policy
*/
public TransactionPolicy getPolicy() {
return policy;
}
/**
* Sets the transaction policy.
* @param policy policy to set.
*/
public void setPolicy( TransactionPolicy policy ) { this.policy = policy;
} /**
* Begins, commits and rolls back the transaction according to the currently
* set policy and EJB spec.
*/
public void intercept( InvocationContext invocationContext ) throws Exception {
UserTransaction newTran = null;
TransactionPolicy thisCallPolicy =
(TransactionPolicy) invocationContext.getOptionalPropertyValue( POLICY_CONTEXT_KEY ); if ( thisCallPolicy == null)
thisCallPolicy = this.policy;
Method method = invocationContext.getTargetMethod();
if ( handlePolicy( thisCallPolicy, invocationContext.getTargetObject(),
method, invocationContext.getParamVals() ) ) {
newTran = getUserTransaction();
log( method, "Begin transaction" );
newTran.begin();
}
try {
invocationContext.proceed();
commitOrRollback( newTran );
} /* Here we try to follow the guidelines of chapter 18 of EJB spec
* with the exception of javax.transaction.TransactionRolledback
* Note that ExceptionHandler already provides logging and wrapping services
* so we don't need to do it here
*/
catch( Exception exception ) {
if ( MockContainer.isSystemException( exception )) {
// If this transaction was started immediately before this invocation
if ( newTran != null && ( newTran.getStatus()==Status.STATUS_ACTIVE ||
newTran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) ) {
newTran.rollback();
log( method, "Rollback because of system exception" ); }
}
// if it is application exception
// we should rollback/commit only if it was new transaction context
else {
// TODO: according to the spec we must ignore the exceptions from the
// rollback if we try to rollback in the catch block of the business
// exception. We should re-throw business exception
try {
commitOrRollback( newTran );
}
catch ( RollbackException rollbackEx ) {
logger.error(
"There has been rollback exception trying to rollback the transaction set for rollback. Ignoring. ", rollbackEx);
}
}
throw exception;
}
} private void commitOrRollback( UserTransaction tran ) throws
SystemException, RollbackException, HeuristicMixedException,
HeuristicRollbackException {
if ( tran != null ) {
if ( tran.getStatus()==Status.STATUS_ACTIVE ) {
tran.commit();
log( "Committing transaction" );
}
else if ( tran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) {
tran.rollback();
log( "Rollling back transaction" );
}
}
}
/**
* Performs the actions necessary to handle the transaction policy
* according to the spec.
* Determines whether the new transaction has to begin for the given method.
* @param policy policy of this invoker
* @param targetObj bean being called
* @param method method being called
* @param args parameter values of the method being called
* @return true if the new transaction must begin for the given method
*/
// TODO: We use TransactionRequiredLocalException even for remote EJBs
protected boolean handlePolicy( TransactionPolicy policy, Object targetObj,
Method method, Object[] args) throws SystemException, NamingException {
boolean newTranRequired = false;
if ( policy == TransactionPolicy.REQUIRED ){ UserTransaction tran = getUserTransaction();
newTranRequired=( tran == null ||
tran.getStatus()==Status.STATUS_NO_TRANSACTION ||
tran.getStatus()==Status.STATUS_COMMITTED ||
tran.getStatus()==Status.STATUS_ROLLEDBACK ||
tran.getStatus()==Status.STATUS_UNKNOWN );
}
else if ( policy == TransactionPolicy.REQUIRED_NEW ){
newTranRequired = true;
}
else if ( policy == TransactionPolicy.MANDATORY ){ UserTransaction tran = getUserTransaction();
if ( tran==null ||
tran.getStatus()==Status.STATUS_NO_TRANSACTION ||
tran.getStatus()==Status.STATUS_COMMITTED ||
tran.getStatus()==Status.STATUS_ROLLEDBACK ||
tran.getStatus()==Status.STATUS_UNKNOWN ) {
throw new TransactionRequiredLocalException(
"Attempt to invoke method with Mandatory policy without transaction context");
}
}
else if ( policy == TransactionPolicy.NEVER ){ UserTransaction tran = getUserTransaction();
if ( tran != null ||
tran.getStatus()==Status.STATUS_ACTIVE ) {
throw new EJBException(
"Attempt to invoke method with Never policy inside transaction context");
}
}
// For Supports we don't need to do anything
// NotSupported is the same deal since we can't suspend the transaction
return newTranRequired;
}
* Returns UserTransaction object. If <code>setUserTransaction()</code>
* was called, will return the object that was set by this method.
* Otherwise, tries to obtain UserTransaction object from JNDI.
*
* @return UserTransaction object
*/
public static UserTransaction getUserTransaction() {
UserTransaction userTransaction = null;
if ( sharedUserTransaction != null ) {
userTransaction = sharedUserTransaction;
}
else {
// obtain tran from the JNDI try { Context context = new InitialContext();
userTransaction = (UserTransaction) context.lookup( USER_TRANSACTION_JNDI );
}
catch ( NamingException namingEx ){
throw new MockEjbSystemException(
"Errors while trying to obtain javax.transaction.UserTransaction from JNDI", namingEx );
}
}
return userTransaction;
} /**
* Sets the shared instance of UserTransaction that will be used by MockEJB.
* This is convenient when the remote JNDI is used and the cost of obtaining
* UserTransaction object from JNDI every time is too high. TransactionManager tries to
* get UserTransaction for every EJB method call.
*
*/
public void setUserTransaction( UserTransaction userTransaction ) {
sharedUserTransaction = userTransaction;
}
private void log( Method method, String message ){ log( message+" for \n"+method );
}
protected void log( String message ){
logger.debug(message);
}
/**
* Returns true if the given object is of the same type and
* it has the same transaction policy.
*/
public boolean equals( Object obj ){
if ( ! (obj instanceof ClassPatternPointcut) )
return false;
TransactionManager transactionManager = (TransactionManager) obj;
return ( this.policy == transactionManager.policy );
} public int hashCode() {
return policy.hashCode();
}
}