javajbossejbjndiejb-3.0

JNDI lookup of EJB with new InitialContext each time?


We use EJB3 and JBOSS application Server in our Application. I have a Bean lookup utility method where its a generic method written to lookup stateless EJB Beans by JNDI name:

public class BeanFactory {

    static  Logger logger = LogManager.getLogger(BeanFactory.class.getName());
    /**
     * 
     * @param jndiName
     * @return
     */
    public static <T> T lookup(String jndiName){

        logger.info("Inside bean BeanFactory lookup: " + jndiName);
        
        T handle = null;

        try {
            InitialContext ctx = new InitialContext();
            handle = (T) ctx.lookup(jndiName);
        } catch (Exception e) {
            logger.error(e, e.fillInStackTrace());
        }
        return handle;
    }

So there are classes which have dependencies on Beans and they use lookup method to invoke the methods of the Bean. For Example

 private AuthenticationProfileDTO getAuthenticationProfile(String credId) throws OneM2MException {
            
            ResourceProceduresDao dao = BeanFactory.lookup(ResourceProceduresDao.JNDI_NAME);
            
            AuthenticationProfileRemote apRemote = BeanFactory.lookup(AuthenticationProfileRemote.JNDI_NAME);
            
            AuthenticationProfileDTO authenticationProfileDTO;
    
            if (isKpsaId(credId))
                authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForSymmKeyID(credId);
            else
                authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForCredentialID(credId);
            return authenticationProfileDTO;
        }

So now when we ran JProfiler on the code the lookup method is coming to be time consuming because every time lookup is called a new InitialContext is instantiated.

I was thinking of making the InitialContext static so that only once it's initialized in a static block, but I don't know what implications will it have in terms of getting Bean instances. Since this piece of code is managed by EJB Container, the run time impacts are unknown. After looking up some articles online not much clarity was there.

Any help is appreciated.


Solution

  • Note that javadoc for InitialContext warns that:

    An InitialContext instance is not synchronized against concurrent
    access by multiple threads. Multiple threads each manipulating a
    different InitialContext instance need not synchronize.
    Threads that need to access a single InitialContext instance
    concurrently should synchronize amongst themselves and provide the
    necessary locking.
    

    So, making the field static isn't necessarily a good idea as you'll need to synchronize each lookup(jndiName) call, and this may cause other issues as per comment by James R. Perkins.

    However as you have shown that getAuthenticationProfile(String credId) calls lookup twice, there is no reason why you can't make a BeanFactory hold one InitialContext to reduce the number of instances by re-using InitialContext within same calling methods.

    public class BeanFactory {
    
        private final InitialContext ctx;
        private BeanFactory(InitialContext initialContext) {
            this.ctx = initialContext;
        }
        private static final Logger logger = LogManager.getLogger(BeanFactory.class.getName());
        /** JNDI lookup*/
        public <T> T lookup(String jndiName){
            // logger.info("Inside bean BeanFactory lookup: " + jndiName);
            try {
                return (T) ctx.lookup(jndiName);
            } catch (Exception e) {
                RuntimeException re = new RuntimeException("Could not find jndi: "+jndiName, e);
                logger.error(re);
                throw re;
            }
        }
        /** Setup a new BeanFactory */
        public static BeanFactory create() {
           try {
                return new BeanFactory(new InitialContext());
            } catch (Exception e) {
                throw new RuntimeException("Could not create a new context", e);
                logger.error(re);
                throw re;
            }
        }
    

    This allows getAuthenticationProfile to use a single InitialContext for 2 lookups:

    BeanFactory ctx = BeanFactory.create();
    ResourceProceduresDao            dao = ctx.lookup(ResourceProceduresDao.JNDI_NAME);
    AuthenticationProfileRemote apRemote = ctx.lookup(AuthenticationProfileRemote.JNDI_NAME);
    

    You might also consider whether saving BeanFactory as a thread local would help though I would be very concerned about doing this an application server because you may have little control over which and how many threads instantiate InitialContext and what from what context they run. However it might be suitable within a standalone client program accessing your EJB server logic:

    private static final ThreadLocal<BeanFactory> BEANS = ThreadLocal.withInitial(BeanFactory::create);
    private static BeanFactory local() {
        return BEANS.get();
    }
    // Example lookups:
    ResourceProceduresDao            dao = BeanFactory.local().lookup(ResourceProceduresDao.JNDI_NAME);
    AuthenticationProfileRemote apRemote = BeanFactory.local().lookup(AuthenticationProfileRemote.JNDI_NAME);