Adam Bien's Weblog
How To Self-Invoke EJB 3.x with(out) "this"
EJB 3.1 are "POJOs with built-in aspects". You get transactions, security, concurrency and monitoring for free with only negligible overhead and without any XML configuration. The aspects, however, can only work in case the container is able to intercept the calls.
Call interception works if the injected (or looked-up) instances are used. this keyword does not work--the call is obviously not intercepted.
To get "this with aspects" you will have to use an injected (or looked-up) instance or use the SessionContext#getBusinessObject method:
@Named
@Stateless
public class Hack {
@Resource
SessionContext sc;
Hack me;
@PostConstruct
public void init(){
this.me = this.sc.getBusinessObject(Hack.class);
}
@TransactionAttribute(value= TransactionAttributeType.NOT_SUPPORTED)
public String boundaryMethodWithoutAspects(){
this.expectsException();
return "...just an ordinary call";
}
@TransactionAttribute(value= TransactionAttributeType.NOT_SUPPORTED)
public String boundaryMethodWithAspects(){
try{
this.me.expectsException();
return "...exception expected :-(";
}catch(EJBTransactionRequiredException e){
return "Works! : " + e;
}
}
@TransactionAttribute(value= TransactionAttributeType.MANDATORY)
public void expectsException(){
System.out.println("Should not appear in the log");
}
}
The execution of the boundary… methods leads to the following output:
boundaryMethodWithoutAspects(): Without aspects: ...just an ordinary call boundaryMethodWithAspects(): With aspects: Works! : javax.ejb.EJBTransactionRequiredExceptionEJB 3 Self-invocation with aspects should not be considered as a best practice. Instead of using the "pattern" described here, you should factor out the method into another bean and introduce a Control The sample project "SelfInvokingEJB" was pushed into:http://kenai.com/projects/javaee-patterns/
Posted at 08:10AM Feb 20, 2012 by Adam Bien in Real World Java EE Patterns - Rethinking Best Practices | Comments[7] | Views/Hits: 8057
NEW Workshop: "JPA, NoSQL, Caching, Grids and Distributed Caches with Java EE 7", May 7th, 2013, Airport Munich
A book about rethinking Java EE Patterns
Tweet Follow @AdamBien

The problem you are pointing out is quite important IMHO. I think a lot of developers are unaware that @RolesAllowed and @TransactionAttribute are not evaluated if you just call the method. That will certainly lead to interesting problems in production. In particular as security and transactions are hard to test. I don't think refactoring into a different class is always the solution - What is wrong with reusing a piece of business logic in a Service?
Posted by Eberhard Wolff on February 20, 2012 at 11:25 AM CET #
Thanks for your comment!
"What is wrong with reusing a piece of business logic in a Service?"
Plain "this" is o.k. but:
usually "this with aspects" is used to cascade transactions. This can be realized lot cleaner with factoring out the "Control" from the "Boundary".
But you are right: there is nothing wrong in general,
thanks,
adam
Posted by Adam Bien on February 20, 2012 at 12:06 PM CET #
I didn't know that, thanks
Posted by Tom on February 20, 2012 at 02:29 PM CET #
What about self-injection like this:
@Inject
Hack me;
Posted by shinzey on February 21, 2012 at 10:48 AM CET #
Is removing the @PostConstruct annotation and using DI to let the container inject a reference to another instance of the same bean an alternative?
@EJB
Hack me;
Posted by Gerhard Dickescheid on February 22, 2012 at 09:43 AM CET #
@shinzey, Gerhard:
@Inject does not work. Weld throws an exception on startup, saying "WELD-001443 Pseudo scoped bean has circular dependencies."
@EJB does not work properly, either. The first session bean gets a properly injected "me". For the next session bean (in me.expectsException()), however, "me" is null. I tried two variants: NOT_SUPPORTED calls MANDATORY and REQUIRED calls NEVER.
Tested on JBoss AS 7.1.
Conclusion: does work for singletons but not for stateless session beans. At least on JBoss, I don't know what the spec defines.
Posted by Marc on March 20, 2012 at 06:16 PM CET #
Oops, correction: does not work with singletons either. At least not on JBoss.
Interestingly enough, it created two instances of my singleton session bean. Oops!?
Source code (unformatted, sorry):
import javax.ejb.EJB;
import javax.ejb.EJBTransactionRequiredException;
import javax.ejb.Singleton;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
@Singleton
public class SelfInvokerSingletonAtEjb {
@EJB
SelfInvokerSingletonAtEjb thisBean;
// Initialized for both "singleton" instances!
private Object hugeAmountOfData = new Object();
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public String boundaryMethod() {
try {
return thisBean.mandatoryTx();
} catch (EJBTransactionRequiredException e) {
return "Works! : " + e;
}
}
@TransactionAttribute(TransactionAttributeType.MANDATORY)
private String mandatoryTx() {
return "Does not work: unexpected invocation.";
}
}
Posted by Marc on March 20, 2012 at 06:33 PM CET #