Search code examples
javajakarta-eeconcurrencyejbcdi

Concurrent access and scaling of "non-managed" utility class


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?
  • How can I make the EE injection approach work?

Solution

  • 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 CDI Instance to programmatically obtain a managed instance of any class you want. You can now add something like this to Container:

    @Inject @Dependent Instance<Stateful> stateful;
    
        @PostConstruct
         public void createStateless(){
             //instantiate sfList;
             sfList.add(stateful.get()); //execute as many times as you need
         }