Adam Bien's Weblog

Monday Feb 20, 2012

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/ 


Special Events: Java 8 with Java EE 7: "More Power with Less Code", 13th October, 2014 and Java EE 7: "Testing and Code Quality", 14th October, 2014

A book about rethinking Java EE 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 #

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