Hi,
I’ve been struggling with this issue for a couple of days now. It happens occasionally when users are added or removed from Grid using methods grid.getContainerDataSource().removeItem(user) or grid.getContainerDataSource().addItem(user). My project is set up with Vaadin CDI so the View containing the Grid is a @CDIView and it implements BroadcastListner interface as described here https://vaadin.com/docs/v8/framework/articles/BroadcastingMessagesToOtherUsers.html When I add a new user a service method responsible for adding a user to database is invoked. If transaction succeeds Broadcaster broadcasts a notification so that other user can see the changes.
It is implemented like that:
public void onUserAdded(@Observes(during=TransactionPhase.AFTER_SUCCESS) UserAddedEvent evt) {
Broadcaster.broadcast(new UserAddedNotification(evt.getUserId(), evt.getUsername(), evt.getPassword()));
}
public void onUserDeleted(@Observes(during=TransactionPhase.AFTER_SUCCESS) UserDeletedEvent evt) {
Broadcaster.broadcast(new UserDeletedNotification(evt.getUserId()));
}
And in the view that is supposed to receive the notification I have:
@Override
public void receiveBroadcast(AbstractNotification notification) {
getUI().access(() -> {
if (notification instanceof UserChangedNotification) {
updateUsersGrid((UserChangedNotification) notification);
}
});
}
UserAdded and UserDeleted notifications inherit from UserChangedNotification and the method updateUsersGrid has the logic for adding or deleting items from Grid.
In the Broadcaster I am using a SingleThreadExecutor as suggested, the static method broadcast(AbstractNotification notification) is synchronized and the code in the View is enclosed in UI.access(). I’ve been experimenting with manual locking like VaadinSession.getCurrent().getLockInstance().lock() but with no success. I have assertions enabled on my server, but i don’t get any AssertionError when exception happens. From time to time when I add/delete a user a ConcurrentModificationException is thrown with a following stacktrace:
21:37:15,814 ERROR [org.jboss.threads.errors]
(default task-3) Thread Thread[default task-3,5,main]
threw an uncaught exception: java.lang.RuntimeException: java.util.ConcurrentModificationException
at io.undertow.servlet.spec.ServletContextImpl.invokeRunnable(ServletContextImpl.java:1029)
at io.undertow.servlet.spec.AsyncContextImpl.onAsyncComplete(AsyncContextImpl.java:614)
at io.undertow.servlet.spec.AsyncContextImpl.access$100(AsyncContextImpl.java:73)
at io.undertow.servlet.spec.AsyncContextImpl$3.run(AsyncContextImpl.java:316)
at io.undertow.servlet.spec.AsyncContextImpl$6.run(AsyncContextImpl.java:485)
at io.undertow.servlet.spec.AsyncContextImpl$TaskDispatchRunnable.run(AsyncContextImpl.java:604)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
at io.undertow.servlet.util.IteratorEnumeration.nextElement(IteratorEnumeration.java:44)
at org.atmosphere.cpr.AtmosphereRequest.getAttributeNames(AtmosphereRequest.java:1049)
at org.jboss.weld.module.web.context.beanstore.http.RequestBeanStore.getAttributeNames(RequestBeanStore.java:47)
at org.jboss.weld.contexts.beanstore.AttributeBeanStore.getPrefixedAttributeNames(AttributeBeanStore.java:239)
at org.jboss.weld.contexts.beanstore.AttributeBeanStore.fetchUninitializedAttributes(AttributeBeanStore.java:120)
at org.jboss.weld.contexts.beanstore.AttributeBeanStore.attach(AttributeBeanStore.java:107)
at org.jboss.weld.module.web.context.http.HttpRequestContextImpl.associate(HttpRequestContextImpl.java:65)
at org.jboss.weld.module.web.context.http.HttpRequestContextImpl.associate(HttpRequestContextImpl.java:41)
at org.jboss.weld.module.web.servlet.HttpContextLifecycle.requestInitialized(HttpContextLifecycle.java:243)
at org.jboss.weld.module.web.servlet.WeldInitialListener.requestInitialized(WeldInitialListener.java:152)
at io.undertow.servlet.core.ApplicationListeners.requestInitialized(ApplicationListeners.java:246)
at io.undertow.servlet.spec.AsyncContextImpl.setupRequestContext(AsyncContextImpl.java:706)
at io.undertow.servlet.spec.AsyncContextImpl.access$700(AsyncContextImpl.java:73)
at io.undertow.servlet.spec.AsyncContextImpl$7.run(AsyncContextImpl.java:619)
at io.undertow.servlet.spec.ServletContextImpl$2.call(ServletContextImpl.java:181)
at io.undertow.servlet.spec.ServletContextImpl$2.call(ServletContextImpl.java:178)
at io.undertow.servlet.spec.ServletContextImpl.invokeRunnable(ServletContextImpl.java:1027)
... 10 more
At this point I don’t know how this can be solved. The app is run on Wildfly 17 and my UI class is annotated with @Push(transport = Transport.WEBSOCKET_XHR). Without using @Push the exception is gone, but the Grid is obviously not updated instantly. Can some Vaadin expert provide support please?