Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CDI + EJB 3 + EJB Transaction

Tags:

cdi

ejb

I need to audit invocations of ejb beans. Saying audit I mean write informations such as current logged user, method name, additional description to a database. I decided to do it by use of CDI decorator:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Override
  public Account createAccount(Account account) {
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

and the decorated class:

@Stateless
public class AccountServiceBean implements AccountService {

   @Override
   public Account createAccount(Account account) {
     ... 
   }
}

Now if I call AccountService from another ejb stateless bean, what will happen with transaction?:

@Stateless
public ApplicationFacadeBean implements ApplicationFacade {

  @EJB
  private AccountService accountService;

  @Override
  public Account createAccount(Account account) {
    return accountService.createAccount(account);
  }

}

I wanted to log transaction status in decorator (AccountServiceBeanDecorator) and decorated class (AccountServiceBean), so I injected TransactionSynchronizationRegistry as a resource in both classes:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Resource
  private TransactionSynchronizationRegistry reg;

  @Override
  public Account createAccount(Account account) {
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

and

@Stateless
public class AccountServiceBean implements AccountService {

   @Resource
   private TransactionSynchronizationRegistry reg;

   @Override
   public Account createAccount(Account account) {

    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
     ... 
   }
}

I received strange behavior:

  • log from decorator

    tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@68fb15d0]]] 
    
  • NullPointerException on second log (reg is null).

Can anybody explain it to me? Wheter AccountServiceBean class is called within the same transaction as ApplicationFacade?

Thank you

like image 718
Marcin Wiśniewski Avatar asked Nov 18 '25 20:11

Marcin Wiśniewski


1 Answers

first: i would not mixing ejbs with cdi interceptors. ejbs has it on interceptor implementations.

second: interceptors are executed in the same transaction as the ejb where the interceptor is around.

possible solution:

  • create a correct ejb interceptor
  • put the interceptor around the method / class
  • create a second ejb (MyLoggerBean) with a method like this logToDatabase(String message) and annotate this method with @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  • inside the interceptor create a class member like this: @EJB private MyLoggerBean loggerBean
  • inside your @AroundInvoke annotated method you could call loggerBean. logToDatabase(...)

this would create a new transaction from inside the current transaction of the ejb where the interceptor is around

--> i know my english is not very good. but i hope that you understand what i think should work. if i have the time, i make e example on github...

like image 62
StefanHeimberg Avatar answered Nov 20 '25 13:11

StefanHeimberg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!