Exception with UiEventPublisher

Hi!

I recently migrated to 2.2.1 and switched from a custom UI-event-broadcaster to the new publishEventForUsers()-method in UiEventPublisher.

Here is my implementation of this feature:

Custom Event definition

public static class AppointmentStatusChangedEvent extends ApplicationEvent
{
    public AppointmentStatusChangedEvent(Object source) { super(source); }
}

Event publishing

@EventListener
public void onAppointmentChangedAfterCommit(final EntityChangedEvent<Appointment> event)
{
    if(event.getChanges().hasChanges() && event.getChanges().isChanged("consultationStatus"))
        uiEventPublisher.publishEventForUsers(new AppointmentStatusChangedEvent(this), null);
}

Event-listener in View

@EventListener
public void onAppointmentStatusChangedEvent(AppointmentEventListener.AppointmentStatusChangedEvent event)
{
    //do stuff in view
}

This works fine locally, but for some reason I get the following Exception regularly in production (application runs inside a docker container on Ubuntu 22.04.4 LTS):

org.springframework.security.core.userdetails.UsernameNotFoundException: User not found
    at io.jmix.securitydata.user.AbstractDatabaseUserRepository.loadUserByUsername(AbstractDatabaseUserRepository.java:186)
    at io.jmix.core.security.impl.SystemAuthenticationProvider.authenticate(SystemAuthenticationProvider.java:48)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
    at io.jmix.core.security.impl.SystemAuthenticatorImpl.begin(SystemAuthenticatorImpl.java:75)
    at io.jmix.core.security.impl.SystemAuthenticatorImpl.runWithUser(SystemAuthenticatorImpl.java:117)
    at io.jmix.flowui.UiEventPublisher.sendEventToUserSessions(UiEventPublisher.java:102)
    at io.jmix.flowui.UiEventPublisher.publishEventForUsersInternal(UiEventPublisher.java:85)
    at io.jmix.flowui.UiEventPublisher.onUiUserEvent(UiEventPublisher.java:80)
    at jdk.internal.reflect.GeneratedMethodAccessor427.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:365)
    at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:237)
    at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:168)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:451)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:384)
    at io.jmix.core.cluster.ClusterApplicationEventPublisher.onAppEventMessage(ClusterApplicationEventPublisher.java:56)
    at io.jmix.core.cluster.LocalApplicationEventChannelSupplier$1.lambda$send$0(LocalApplicationEventChannelSupplier.java:52)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at io.jmix.core.cluster.LocalApplicationEventChannelSupplier$1.send(LocalApplicationEventChannelSupplier.java:52)
    at org.springframework.messaging.MessageChannel.send(MessageChannel.java:45)
    at io.jmix.core.cluster.ClusterApplicationEventPublisher.publish(ClusterApplicationEventPublisher.java:70)
    at io.jmix.flowui.UiEventPublisher.publishEventForUsers(UiEventPublisher.java:160)
    at at.hi1.terminvergabetool.listener.AppointmentEventListener.onAppointmentChangedAfterCommit(AppointmentEventListener.java:51)
...

Jmix version: 2.2.1
Jmix Studio plugin version: 2.2.1-233

Any ideas what could cause this issue?

Thanks!

It seems we have found the cause for this Issue:

when the method publishEventForUsers() is given null as second parameter (List of usernames), it will load all active sessions and fetch all corresponding Users via AbstractDatabaseUserRepository.

We also use the Multitenancy-addon for our project, so the AbstractDatabaseUserRepository potentially attempts to load Users from a tenant that is different from the one that triggered the event, leading to the UsernameNotFoundException mentioned in the original post.

If anyone else encounters a similar problem: we fixed it by manually loading all users/usernames for the tenant that triggers the event and pass it to the publishEventForUsers()-method.

List<String> userNames = dataManager.load(User.class).all().list().stream().map(User::getUsername).toList();

uiEventPublisher.publishEventForUsers(new AppointmentStatusChangedEvent(this), userNames);
1 Like