Updating UI with push from a background thread causes reload

I’m using Vaadin 23 to test real-time UI updates with message distribution over JMS in a WildFly 26 (Jakarta EE 8) cluster. There is a Vaadin view that allows sending and receiving messages across the cluster. The view broacasts the messages using the broadcaster pattern. Besides broadcasting to local listeners, the broadcaster relies on CDI events and the jakartaee-clustered-cdi-events EJB module to send the events to other cluster nodes via JMS. Local broadcasts update the view as expected, but when a message arrives from another node over JMS, the Vaadin view reloads unexpectedly. Vaadin 23 completely fails to show the messages, while Vaadin 8 is able to show the messages after page reload (I also created a Vaadin 8 version of the application). See attached screencasts below that demonstrate this behavior.

Here is the test application:

Here is the Vaadin view, the broadcaster and it’s base class:

If you are interested, you can test it by running the cluster with Docker Compose as follows:

I suspect I might be missing something obvious. Can anyone provide any hints on how to fix this?

Here’s a diagram that gives an overview of how the components interact:

Here’s how the Vaadin 23 application behaves. First two windows from left are two different user sessions on the same node, the rightmost window is on a different node. Broadcast between different users on the same node works, but completely fails between nodes.

Here’s how the Vaadin 8 application behaves. Here both windows are on different nodes. You can see messages moving between nodes successfully after page reload.

Finally, here’s the Vaadin 8 application debug log from browser console that has a couple of interesting lines. Notably:

Thu Jan 18 21:01:32 GMT+200 2024 com.vaadin.client.communication.XhrConnection
INFO: Received xhr message: for(;;);[{"changes":{},"resources":{},"locales":{},"meta":{"appError":{"caption":"Session Expired","url":null,"message":"Take note of any unsaved data, and <u>click here</u> or press ESC key to continue.","details":null}},"syncId":-1}] line 9 > injectedScript:1:245
Thu Jan 18 21:01:32 GMT+200 2024 com.vaadin.client.communication.MessageHandler
SEVERE: Response didn't contain a server id. Please verify that the server is up-to-date and that the response data has not been modified in transmission. line 9 > injectedScript:1:160

message.txt (5.34 KB)

And here is the Vaadin 23 application debug log that doesn’t have much useful information, unlike in Vaadin 8. I’m missing the detailed log of Vaadin 8…
message.txt (2.19 KB)

There’s nothing interesting in the backend logs.

I just skipped over it, this sounds like worth a GitHub issue :sweat_smile:

Thanks for the suggestion! I’ll file it gladly, but let’s wait for more comments just in case before. I might be missing something obvious.

Nice report! For clarification, the listener function on the remote node is being called, right?

If so, can you try pushMode=MANUAL and calling push() explicitly?

I think what may be happening is that there is a CDI instance of your view in the CDI context, but it’s not a Vaadin UI-bound CDI instance

If that is the case, can you make the MainView @RequestScoped?

I think that the issue might be that yuo are getting the UI instance inside the listener, instead of catching it before the execution.

If you look at the example in the docs, it gets the UI reference from the event and the listener lambda captures the instance

Nice report! For clarification, the listener function on the remote node is being called, right?
Thanks for the good words! Yes, the listener function on the remote node is called, see the attached detailed log.
Here’s a breakdown of the events in the log:

  1. Message received and broadcasted on node 1.

    • AbstractBroadcaster on wildfly-node1 receives a “hello” message.
    • For some reason there are four MainView instances in four threads concurrently receiving this message on wildfly-node1?
  2. Message is sent to the JMS topic on node 1.

    • JMSMessageSender on wildfly-node1 sends the “hello” message to the CLUSTER_CDI_EVENTS JMS topic.
  3. Message received from the JMS topic on node 1.

    • JMSMessageReceiver on wildfly-node1 receives the message from the JMS topic
    • Message to self is detected, no further processing occurs on node 1.
  4. Message received from the JMS topic on node 2.

    • JMSMessageReceiver on wildfly-node2 also receives the message from the JMS topic.
    • AbstractBroadcaster on wildfly-node2 logs the receipt of the message.
    • Again for some reason 3 MainView instances in seprate threads concurrently receive the message on wildfly-node2.
  5. Views are reloaded.

    • AbstractBroadcaster on both nodes logs the registration of new MainView listeners. The registration is called from the onAttached() event handler.
      message.txt (3.45 KB)

I later noticed that new copies of the views are registered every 5 minutes (during each heartbeat):

test-jakartaee-clustered-cdi-events-wildfly-node1-1  | 17:52:14,037 INFO  [org.test.AbstractBroadcaster] (default task-5) org.test.Broadcaster@34b9caae registering org.test.MainView$$Lambda$1902/0x0000000101533440@2814248e
test-jakartaee-clustered-cdi-events-wildfly-node1-1  | 17:57:14,162 INFO  [org.test.AbstractBroadcaster] (default task-8) org.test.Broadcaster@34b9caae registering org.test.MainView$$Lambda$1902/0x0000000101533440@588616bc
test-jakartaee-clustered-cdi-events-wildfly-node1-1  | 18:02:14,297 INFO  [org.test.AbstractBroadcaster] (default task-8) org.test.Broadcaster@34b9caae registering org.test.MainView$$Lambda$1902/0x0000000101533440@6de482eb

I didn’t see any detach/unregistration events in the log. The listeners list only keeps growing.

@bonny-elephant

I think what may be happening is that there is a CDI instance of your view in the CDI context, but it’s not a Vaadin UI-bound CDI instance
Considering that the code is copied from the standard CDI starter, wouldn’t this be a highly visible bug?
If that is the case, can you make the MainView @RequestScoped?
I tried (somewhat reluctantly) and deployment fails with error Caused by: org.jboss.weld.exceptions.DeploymentException: Normal scoped Vaadin components are not supported. 'org.test.MainView' should not belong to a normal scope. The error message is quite clear I think. I would rather not change the standard CDI example scopes unless there is a convincing rationale for this. But many thanks for the helpful suggestion :pray: !

@versatile-zorse

If you look at the example in the docs, it gets the UI reference from the event and the listener lambda captures the instance
Thanks for bringing this up, the code matches the official example now: https://github.com/mrts/test-jakartaee-clustered-cdi-events/commit/b38d605c18820e648a4eeb718db5412c48fbcabe
This did not alleviate the problem however.

It’s very odd that a remote event would behave differently from a local event

I mean, you would expect that the only thing that matters would be whether the method is called

The generic CDI event observer methods in AbstractBroadcaster were the culprit. Generic types are erased during runtime, so when using @Observes or @ObservesAsync with a generic type, the CDI container only saw the raw type, which led to the unintended behavior. Real glad it was a simple mistake after all. Seems obvious in hindsight, but all the symptoms were so confusing that I thought it was somehow related to Vaadin. Here’s the fix: https://github.com/mrts/test-jakartaee-clustered-cdi-events/commit/c11dd612945a06df87829455886a939591fb2bf0