If I have a stateful class that requires a utility-like stateless class to perform an operation on it. These stateful classes are kept in a list in a container (stateful) class. This is what I would do with plain Java:
class Container {
List<Stateful> sfList;
}
class Stateful {
List<String> list;
void someMethod() {
list.add("SF");
Stateless.foo(this);
}
class Stateless {
public static void foo(Stateful sf) {
sf.getList().add("SL");
}
}
and inside main
this is the procedure:
Stateful sf = new Stateful();
sfList.add(sf);
sf.someMethod();
Now with JavaEE CDI I would do:
class StatefulBean {
List<String> list;
@Inject
StatelessBean slsb;
void someMethod() {
list.add("SF");
slsb.add(this);
}
@Stateless
class StatelessBean {
public void add(StatefulBean sfsb) {
sfsb.add("SL");
}
}
In this case all StatefulBean could access a shared pool of StatelessBean with no concurrency issues and it will scale properly with requests.
However, since Stateful
is not a managed bean I can't inject into it and so I used the utility class instead. Also I'm creating Stateful
with a constructor so I can't inject into it the stateless bean (I will get a NPE using it).
My questions are:
Are there concurrency and scalabilty differences between the stateless injection approach (provided it would work) and the utility class approach?
Yes there are, primarily around the loss of management. The way you're instantiating Stateful
on every invocation of the method, there's no pooling involved. This is going to lead to you creating more instances than you probably need.
Another loss is on the scalability side. Where the container will manage the passivation and activation of your stateful bean in a distributed environment, the manual approach will see to it that you manage your own activation and passivation.
Since
Stateful
is not a managed bean..
Incorrect. According to the CDI Spec, any java class that meets the listed criteria (in your case, a default no-arg constructor) is a managed bean. That means that you could @Inject
StatelessBean
into Stateless
and the container would oblige. To allow this level of management, you'll need to set bean-discovery-mode=all
in your beans.xml.
Even with the (apparently needless) circular reference, normal Java concurrency rules apply: as long as the state that you're manipulating is not static or in a static class, you're threadsafe. Each threaded call to that static method still operates on a separate stack, so no problems there.
How can I make the EE injection approach work? If you need on-demand instantiation of
Stateless
(or any other bean really), use the CDIInstance
to programmatically obtain a managed instance of any class you want. You can now add something like this toContainer
:
@Inject @Dependent Instance<Stateful> stateful;
@PostConstruct
public void createStateless(){
//instantiate sfList;
sfList.add(stateful.get()); //execute as many times as you need
}