0
votes

I have an application running on JBoss / Wildfly. I use CDI event to communicate between modules. I would like to be able to use events as a way to plug in customisations, basically dynamically loading an additional observer that would also receive CDI Events.

Here's a snippet I use to load classes dynamically from within an EJB:

File file = new File("D:\\workspace\\integrator\\target\\classes\\");
            
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};

ClassLoader cl = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());

Class cls = cl.loadClass("demo.DemoObserver");
observerInstance=cls.newInstance();

The loaded class is very simple, just a normal CDI event observer that is annotated as @Dependent to be "seen" as a bean. My beans.xml is configured to automatically discover beans.

@Dependent
public class DemoObserver {
    
    private boolean observed=false;
    
    public DemoObserver() {
        observed=false;
    }
    
    @PermitAll
    public void stateChanged(@Observes EquipmentE10StateChanged event) {
            observed=true;
            System.out.println("Demo observer received event E10 state changed: " + event.toString());
    }

}

The class is loaded and instantiated (I can see logs coming from its constructor), but it never receives events.

Is there anything I can do to get this working?

1
Do you want the dynamic observer to appear at an arbitrary time (e.g. user adds a jar with observers at runtime, without interrupting the life of the application), or is this happening at application start (e.g. when the application launches, it scans its "environment" for observers and adds whatever is found, but after startup they do not change)? I think CDI portable extensions will suit the latter case. For the first, I am not sure, maybe you need something like CDI-OSGi integration.Nikos Paraskevopoulos
Nikos, thanks for the reply. I was actually hoping for a way to do that at runtime, because the app runs 24x7 for automation purposes.j-dimension
When you are already in a JEE environment, you have much better tools at hand than simple CDI events. Laird already told you all about CDIs limitations in this regard in his excellent answer. My recommendation is: have a look at the messaging features in the server (i.e. set up a message queue/topic, dynamically add and remove jms based listeners) and see whether this suits your problem statement.mtj

1 Answers

6
votes

There are many ways to answer the question that is really being asked but for the moment I'll stick to question as asked.

Because CDI places an emphasis on type safety, it computes the wiring between beans (and observer methods etc.) at startup and then locks it down. So in general if you have a class-that-is-annotated-to-be-a-bean and for whatever reason it is not "seen" at startup, you can't "make" it be "seen" later. If, after portable extensions have run and the container is up and running, you have four observer methods, then that is forevermore the total number of observer methods that will exist. This is by design.

(Also, in your example, you may indeed have a bean class but you are creating it yourself by hand with your own newInstance() invocation. In CDI, in general, if you are doing something with new, chances are you're doing it wrong™ at some level.)

This means that assuming you know nothing else at startup, you'll need an observer method that observes the most abstract kind of event that you're interested in, and then does its own dynamic dispatching to whatever it is that really needs to be notified.

Alternatively, if you know, at startup, the "closed world" of potential observers—perhaps there is EquipmentE10 observers and EquipmentE09 observers and that's it—you can create an observer method suitable for each of them and use some combination of event subtyping and qualifiers to choose which ones get notified. However, do note that event qualifiers and payloads are somewhat counterintuitive.