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.EJBTransactionRequiredException

EJB 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/ 

Comments:

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 12:25 PM 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 01:06 PM CET #

I didn't know that, thanks

Posted by Tom on February 20, 2012 at 03:29 PM CET #

What about self-injection like this:

@Inject
Hack me;

Posted by shinzey on February 21, 2012 at 11: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 10: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 07: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 07:33 PM CET #

I have an abstract EJB that provides a methoded annoated with @Schedule.
Inherited classes are singletons (@Singleton, @Startup).
The method annotated with @Schedule should run without Transaction context but the business functioanlity in abstract class requires a transaction context. Therefore I need a self-invoke on the abstract class. But since the children are EJB-singletons this would not work. Any other ideas how to solve this?

Posted by Matthias on October 14, 2014 at 02:36 PM CEST #

@Inject Instance<SameEjb> self; works ok

Posted by 83.6.214.19 on November 14, 2014 at 05:36 PM CET #

I'm afraid the self referencing in the PostConstruct leads to an infinitive recursion and a bulk of business objects. I guess an getter which receive the business object on demand is a better way.

Posted by Ludger on May 06, 2015 at 01:01 PM CEST #

The funny thing is: I'm sure I've done this a lot of times via injection. At least on Glassfish 3/4, it just works.

Guess we should check the specs, but this is both valid to me for EJB and CDI. Guess those are JBoss & Weld implementation bugs.

Posted by Yannick Majoros on May 07, 2015 at 03:00 PM CEST #

I do it all the time too via self-injection. Glassfish 3/4, Weblogic, JBoss 7.3...

I did check it in the specs years ago. Circular references in EJB's are allowed, no problem with that.

Posted by Yannick Majoros on July 30, 2015 at 10:55 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License