jakarta-eecdijndiejb-3.2

The best way to integrate EJB (JNDI) and CDI


Currently we have a deployment architecture were a bunch of data oriented services are exposed via RMI to business services. Both types (the data oriented services and the business services) are stateless session beans. Each data-service interface package (containing the remote interfaces) also has a locator, that does the JNDI lookup. We so this so that we can call an data oriented service from anywhere in the business service logic.

This is how a locator looks like:

public final class OMRLocator {

    private static final Logger LOG = Logger.getLogger( OMRLocator.class );

    private static final String ORG_WILDFLY_INITIAL_CTX_FACTORY = "org.wildfly.naming.client.WildFlyInitialContextFactory";

    private OMRLocator() {
    }

    @Produces
    public static OrganisationsAndMandatesRegister locate() {
        try {
            Properties ctxProp = new Properties();
            ctxProp.put( Context.INITIAL_CONTEXT_FACTORY, ORG_WILDFLY_INITIAL_CTX_FACTORY );
            InitialContext ctx = new InitialContext( ctxProp );
            return (OrganisationsAndMandatesRegister) ctx.lookup( OrganisationsAndMandatesConstants.REMOTE_NAME );
        }
        catch ( NamingException ex ) {
            LOG.log( Level.WARN, "Cannot reach: " + OrganisationsAndMandatesConstants.REMOTE_NAME, ex );
            return null;
        }
    }
}

We were running on JBOSS EAP6 and started to experiment with CDI. Hence, we added a beans.xml to the data-service-beans and the @Produces to make the (in this case OrganisationAndMandatesRegister CDI injectable. The idea is that the future we might repackage our application and package the data-services together with the business-service in one enterprise archive.

Lately we migrated to JBOSS EAP7.2 (Wildfly 8?) and suddenly we see all kinds of unexpected latency and transaction problems.

My suspicion is that the way we obtain beans are a factor in these problems. For instance: I guess the scope is dependent on the business EJB lifecycle, but for each call to the locate() in the business service a new instance of a data-service is produced.

So: what is the best way to produce a remote bean (via RMI) when using CDI? Should I take scoping into consideration given that both types of services are stateless (or is this done automatically)?


Solution

  • If no scope is defined on the producer method then @Dependend is used, so find the proper scope, maybe @RequestScoped. When you retrieve an EJB from JNDI you do not get a new instace, you get an instance from the pool which could be the same over multiple calls. Your problem could be the EJB interceptors because if dependent scoped the EJB instance is always the same once injected an is never released.

    Get rid of the @Produces because CDI integrates with EJB and EJBs can be injected via @Inject or @EJB. If you want to keep the Locator class then you can inject the EJBs in there and return the proper EJB instance (which is actually a proxy), whereby the Locator should be @ApplicationScoped. Another way is to to use Instance which alows a programmatic lookup. With the type Object you can access all CDI Beans (including EJBs) of the container so a common interface would be useful to minimize accessible beans.

    See the following links for more help.

    https://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#_obtaining_a_contextual_instance_by_programmatic_lookup

    Inject a stateless EJB with @Inject into CDI Weld ManagedBean (JSF 1.2 EJB Application on jboss 6 AS)

    http://www.adam-bien.com/roller/abien/entry/inject_vs_ejb


    Just to summarise:

    option a) Leave as is. Perhaps make scope explicit with @Dependent to indicate this is called at calling-bean-creation (injecting in constructor of calling bean)

    option b) Use a stateless @ApplicationScoped session bean

    @LocalBean // otherwise @EJB will not work
    @ApplicationScoped // this instance should be created only once per ear
    public class OMRLocator {
    
        @EJB // does implicitly a remote (default) JNDI lookup
        private OrganisationsAndMandatesRegister instance;
    
        @Produces
        @Dependent // just to make it explicit
        public OrganisationsAndMandatesRegister locate() {
           return instance;
        }
    }