Search code examples
javajakarta-eecdi

Jakarta CDI force bean construction/register legacy event listeners


I have a new Jakarta EE (Qurakus) application and some old code which has methods to register event listeners. I have made a wrapper for the event registry. Now I need some way to efficiently register a lot of event listeners. It looks something like this:

@ApplicationScoped
public class EventRegistry {
    private Set<Listener<?>> listeners = Collections.synchronizedSet(new HashSet<>());

    public void register(Listener l) {
    }
    public void unregister(Listener l) {
    }
}
@ApplicationScoped
public class Listener1 implements Listener<MyEvent> {
    @Override
    public void onEvent(MyEvent e) {
    }
}

So basically I need a way for the listeners to register. I thought about using a Jakarta EE event which I trigger in PostConstruct of the registry and listen to it in each listener which then registers itself. Is this a save way to do it? Or will the listener bean be destructed if not used through an Inject? Also is there generally a better way to execute an operation for specific beans (I have all the listeners in a single package).


Solution

  • A simple way to do exactly what you want would be the following:

    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.enterprise.inject.Instance;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    @ApplicationScoped
    public class EventRegistry {
        private final Set<Listener<?>> listeners = Collections.synchronizedSet(new HashSet<>());
    
        @Inject
        void init(Instance<Listener<?>> listenerInstance) {
            // check out the comments; for Quarkus you can inject all listeners as:
            // @Inject @All List<Listener<?>> listeners
            listenerInstance.forEach(listeners::add);
        }
    
        public void register(Listener l) { // maybe not even needed anymore
            listeners.add(l);
        }
    }
    

    You can ask CDI to do method injection; you can also ask it for an Instance of a type (in this case your Listener) and through the Instance retrieve all implementations, as shown above. Methods annotated with @Inject get called when injection happens on the instance and before any @PostConstruct methods. You could do property injection (i.e. @Inject private Instance<Listener<?>> listenerInstance), but you only need this object during the initialization of the EventRegistry. There is no reason to keep it around in a property for any longer.

    Since you are in CDI and CDI supports the observer pattern you could consider changing your implementation to use that facility, as suggested in another answer.