Search code examples
javarestjakarta-eesingletoncdi

How to make an singleton instance visible everywhere using CDI?


I have an application. Each user corresponds to the entity "Client". During the session, the user works with his entity.

I have JAX-RS + EJB application. I want this entity to be visible and used in all services, as a singleton. I want to achieve it using CDI.

First, the user login to the application. After login, I want to get its entity from the base and assign it to a singleton, but I didn't succeed. I tried the @Singleton annotations (javax.inject), @SessionScoped and @ApplicationScoped.

How can I do it?

//Entity
@Entity
@SessionScope //or @Singlton/@ApplicationScope
class Client { fields }

//login service
@Inject 
Client client;
//After login I want to assign an entity from db[cleintEntity] 
//to entity which is available everywhere[client]
client = clientEntity;

I want to do this:

//some ejb
@Inject
Client client;
//use entity

I don't want to transmit a link to the instance throughout the application, but I want it to be available everywhere using CDI.

Or do I need to create a separate ClientDAO, which will be my singleton?


Solution

  • Based on your comment, you must use the @SessionScope. The @Singleton and @ApplicationScope could be used if you wanted to have a single instance for all your users.

    To solve your problem:

    1. You want to instantiate the Client when user logs in. So when you're in the login service, this object is not instantiated yet, so you can't @Inject it into your login service and you should remember to remove the @Inject annotation.

    2. You need to use the @Produces annotation. This annotation is used when you want to have control on the way your class is getting instantiated. When CDI container wants to find an implementation of the Client class, if it finds a method that returns Client and has @Produces annotation, it calls that method instead of just instantiating the Client itself. You should do your login business, then create an instance of Client and store it in your login service as a member variable. Then add a method in this class which returns that client object and annotate the method with @Produces. The final structure of your login service would be something like this:

        @SessionScope
        public class LoginService {
            private Client client;
    
            public void login(String username, String password) {
                //  implement login business
    
                //  if login is successful
                client = clientEntity;
            }
    
            @Produces
            @SessionScope
            public Client clientProducer() {
                return this.client;
            }
        }
    

    You can also put the @Produces annotation on top of a field. In such cases, the CDI container will use the value stored on that field instead of calling a method.

        @SessionScope
        public class LoginService {
            @Produces
            @SessionScope
            private Client client;
    
            public void login(String username, String password) {
                //  implement login business
    
                //  if login is successful
                client = clientEntity;
            }
        }
    

    Of course this should be considered as a sort of pseudocode. I don't know about all the details of your business. Maybe the way you're implementing the logic is completely wrong ;). But to solve this specific problem, the @Produces should work.