I inject a Java EE @Stateless
-bean into a @Singleton
/@ApplicationScoped
-bean:
@Stateless
public class MyStateless {
@PersistenceContext
private EntityManager em;
public void persist(User u){
// for each thread the emProxy is the same
log.info("emProxy={0}", em.toString());
// for each thread the emDelegate is differently
log.info("emDelegate={0}", em.getDelegate().toString());
// this is definitly thread-safe !
em.persist(u);
}
}
version 1:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
version 2:
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
version 3:
@javax.ejb.Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this hread safe ?
stateless.persist(new User("hello"));
}
}
And I have the following statement:
"If I inject a @Stateless
-bean into any kind of @Singleton
or @ApplicationScoped
-bean, then for each call of persistUser()
the container pool will provide another (and not necessarily the same) instance of MyStateless
. Thus, there is no 1:1-relation between @Stateless
-bean and the @Singleton
/ @ApplicationScoped
-bean. That also means, the container managed entiyManager
injected in my @Stateless
-bean which is indirectly used by my @Singleton
/@ApplicationScoped
-bean through the method persistUser()
is thread-safe."
Is the above statement right for all 3 versions or will they behave differently with my stateless bean?
And please look at the following special case (version 4):
@javax.ejb.Singleton
public class MySingleton {
@Resource private ManagedScheduledExecutorService managedExecutor;
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
for(int i=0; i<=4;i++){
managedExecutor.scheduleWithFixedDelay(()
-> stateless.persist(new User("hello_" + i)) , 0, 5, TimeUnit.SECONDS);
}
}
}
Is version 4 thread safe? It must, because I use a method from a stateless
-bean as Runnable
and by definition a container managed entityManager
within a stateless
-bean must be thread-safe. Am I right?
Looking at the logs, each thread prints the same entityManager
-proxy:
emProxy = org.jboss.as.jpa.container.TransactionScopedEntityManager@3297c304
but the delegate for each tread is differently (delegate to the underlying persistence provider hibernate):
emDelegate = SessionImpl(2028358710<open>)
The proxy for entityManager
is the same on each thread, but the delegate of the entityManager
-proxy on each thread is differently, hence it is thread-safe. Am I right? Or do I also need different entityManager
-proxies for each thread to be thread-safe?
Btw, there is no difference, if I change MyStateless
to @ApplicationScoped
@ApplicationScoped
public class MyStateless {..}
If I use @ApplicationScoped
, then all simultanously running threads have equal entityManager
-proxies but different entityManager
-delegates. So I cannot see any reason to choose @Stateless
instead of @ApplicationScoped
.
Conclusion:
There are no problems in these cases above:
"The same proxies can be used in simultanously running threads as long as their delegates are different." What these proxies are (entityManager, stateless beans, etc. does NOT matter.)
From the JPA specs (version 2.1, chapter 7.2):
An entity manager must not be shared among multiple concurrently executing threads, as the entity manager and persistence context are not required to be threadsafe. Entity managers must only be accessed in a single-threaded manner.
The MyStateless
bean can be used by many clients, because the application server takes care of the injected EntityManager
. So MyStateless
is thread safe to the outside world; accessing its EntityManager
from manually spawned threads within the container (i.e. threads created not using the application server's facilities, like the ManagedExecutorService
), would be unsafe. I am not even certain that using the same EntityManager
, even from container-managed threads, is safe. Using it concurrently from threads corresponding to multiple clients is, of course, safe. See also in the EJB specs (version 3.2, chapter 4.8.5):
It is legal to store Java EE objects that do not support concurrent access (e.g. references to Java Persistence entity managers or stateful session beans) within the singleton session bean instance state. However, it is the responsibility of the Bean Provider to ensure such objects are not accessed by more than one thread at a time.
With this in mind, versions 1, 2, 3 of accessing MyStateless
are OK, as long as they do not occur in manually spawned threads. Version 4 will be OK as it is only because (a) the threads are container-managed, so the container will make sure to use a different EntityManager
in each and (b) the workloads of each thread are independent - can run in different transactions.
And indeed there is no 1-1 relation between the injection "client" and the MyStateless
injected bean. Actually the container should inject only a proxy to each injection point and the proxy is responsible for resolving the appropriate instance to use.