0
votes

A class which is not an EJB or a CDI (its a simple java pojo as shown in example) needs to lookup programmatically an instance of an RequestScoped bean. To prevent always looking up the same instance I though ouf ThreadLocal

Here is a pseudo code, its kinda like this:

STATIC class aClass {

   public static ThreadLocal<RequestScopedBean> aRcBean;

   public static Integer getDataFromReqBean() {
   if(aRcBean == null) {
      aRcBean = new ....
      aRcBean.set(CDI.current.select.get(RequestScopedBean.class));
   }
   return aRcBean.get() != null ? aRcBean.get().doSomething() : null;
  }
}

My question is am I going to get issues with GC and memory leaks with parallel requests and threadlocal objects not releasing instances to GC?

Its a java ee jax rs project of us. A http post is fired and somewhere in our backend I have this constellation. I cannot change much code. I need a solution to work like I provided in the example above.

1
I wonder, why can't you (or the creators of this code) use standard injection for getting the RequestScopedBean? The code for lazy-initializing the ThreadLocal is incorrect, this section might be called by many threads, each creating its own instance. More seriously, it is not the instance of ThreadLocal that should be created, rather the instance of the RequestScopedBean inside it! Create the ThreadLocal at static init time and check its content every time. Still, this way, future requests that use the same thread will end up with the old RequestScopedBean, which may be very bad. - Nikos Paraskevopoulos
Additionally, are you sure the time spent looking up the CDI instance is significant enough to warrant workarounds like this? If you could describe in more detail what is this code trying to achieve and why, we may be able to suggest alternative approaches! - Nikos Paraskevopoulos
@NikosParaskevopoulos to sum up,each thread gets the proxy instance of the original instance of RequestScopedBean which is there available per request --> thread = http request... I wanted to prevent looking up for the RequstScopedBean each time its needed therefore I thought of ThreadLocal to hold the instance per Reqest. I cannot use any other approach because a static class needs manual bean injection. - dev hedgehog

1 Answers

0
votes

Well. Let's assume your app is running at some JavaEE/JacartaEE App Server like Payara for example. Isn't it?

First, in my oppinion you do not need to put your proxies to threadlocal, you may rather consider keep Local interface view or even No interface view field in your POJO. And so, you can still use Context lookup.

For example. Your Stateless bean:

package stack.overflow.ejb.module.control;

import javax.ejb.Stateless;

/**
 *
 * @author s.kadakov
 */
@Stateless
public class SomeBean {
    
    public String getBeanName() {
        return SomeBean.class.getCanonicalName();
    }
    
}

Your POJO:

package stack.overflow.ejb.module.control;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 *
 * @author s.kadakov
 */
public class SomePojo {

    SomeBean someBean = lookupSomeBeanLocal();

    public SomeBean getSomeBean() {
        return someBean;
    }
    
    private SomeBean lookupSomeBeanLocal() {
        try {
            Context c = new InitialContext();
            return (SomeBean) c.lookup("java:global/ejb-module-1.0-SNAPSHOT/SomeBean");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
            throw new RuntimeException(ne);
        }
    }

}

Hint: you may pick up Portable JNDI name of you bean in application server log when your application deployment. In my case those are:

Portable JNDI names for EJB SomeBean: [java:global/ejb-module-1.0-SNAPSHOT/SomeBean, java:global/ejb-module-1.0-SNAPSHOT/SomeBean!stack.overflow.ejb.module.control.SomeBean]]]

Your JAX-RS resource:

package stack.overflow.ejb.module.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import stack.overflow.ejb.module.control.SomePojo;

/**
 *
 * @author s.kadakov
 */
@Path("javaee8")
public class JavaEE8Resource {
    
    @GET
    public Response ping(){
        
        SomePojo pojo = new SomePojo();
        
        String beanName = pojo.getSomeBean().getBeanName();
        
        return Response
                .ok(beanName)
                .build();
    }
}

So, let' try:

curl http://localhost:8080/ejb-module/resources/javaee8
stack.overflow.ejb.module.control.SomeBean

I tested this snippet at Payara 5 Full, hope it would work same way at others JavaEE/JakartaEE compatible servers.

Hope it will be usefull or will provide some hint for you.