3.2.6.4. Events

See Decouple Business Logic with Application Events guide to learn different examples of application events.

Events bean encapsulates the application-scope event publication functionality. Application events can be used to exchange information between loosely coupled components. Events bean is a simple facade for ApplicationEventPublisher of the Spring Framework.

public interface Events {
    String NAME = "cuba_Events";

    void publish(ApplicationEvent event);
}

It has only one method publish() that receives an event object. Events.publish() notifies all matching listeners registered with this application of an application event. You can use PayloadApplicationEvent to publish any object as an event.

Event handling in beans

First of all, we have to create a new event class. It should extend the ApplicationEvent class. An event class can contain any additional data. For instance:

package com.company.sales.core;

import com.haulmont.cuba.security.entity.User;
import org.springframework.context.ApplicationEvent;

public class DemoEvent extends ApplicationEvent {

    private User user;

    public DemoEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

Beans can publish an event using the Events bean:

package com.company.sales.core;

import com.haulmont.cuba.core.global.Events;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.security.global.UserSession;
import org.springframework.stereotype.Component;
import javax.inject.Inject;

@Component
public class DemoBean {

    @Inject
    private Events events;
    @Inject
    private UserSessionSource userSessionSource;

    public void demo() {
        UserSession userSession = userSessionSource.getUserSession();
        events.publish(new DemoEvent(this, userSession.getUser()));
    }
}

By default, all events are handled synchronously.

There are two ways to handle events:

  • Implement the ApplicationListener interface.

  • Use the @EventListener annotation for a method.

In the first case, we have to create a bean that implements ApplicationListener with the type of our event:

@Component
public class DemoEventListener implements ApplicationListener<DemoEvent> {
    @Inject
    private Logger log;

    @Override
    public void onApplicationEvent(DemoEvent event) {
        log.debug("Demo event is published");
    }
}

The second way can be used to hide implementation details and listen for multiple events in a single bean:

@Component
public class MultipleEventListener {
    @Order(10)
    @EventListener
    protected void handleDemoEvent(DemoEvent event) {
        // handle event
    }

    @Order(1010)
    @EventListener
    protected void handleUserLoginEvent(UserLoggedInEvent event) {
        // handle event
    }
}

By default, Spring events require protected, package or public access modifiers for @EventListener methods. Pay attention that private modifier is not supported.

Methods with @EventListener annotation do not work in services, JMX beans and other beans with interfaces. If you define @EventListener on such bean you will get the following error on application start:

BeanInitializationException: Failed to process @EventListener annotation on bean. Need to invoke method declared on target class, but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.

If you need to listen to an event in a bean with interface, implement the ApplicationListener interface instead.

You can use Spring Framework Ordered interface and @Order annotation for event handlers ordering. All the platform beans and event handlers use order value between 100 and 1000, thus you can add your custom handling before or after the platform code. If you want to add your bean or event handler before platform beans - use a value lower than 100.

See also Login Events.

Event handling in UI screens

Usually, Events delegates event publishing to the ApplicationContext. On the web tier, you can use a special interface for event classes - UiEvent. It is a marker interface for events that are sent to UIs screens in the current UI instance (the current web browser tab).

Please note that UiEvent instances are not sent to Spring beans.

Sample event class:

package com.company.sales.web;

import com.haulmont.cuba.gui.events.UiEvent;
import com.haulmont.cuba.security.entity.User;
import org.springframework.context.ApplicationEvent;

public class UserRemovedEvent extends ApplicationEvent implements UiEvent {

    private User user;

    public UserRemovedEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

It can be fired using Events bean from a window controller the same way as from a bean:

@Inject
Events events;
// ...
UserRemovedEvent event = new UserRemovedEvent(this, removedUser);
events.publish(event);

In order to handle an event you have to define methods in UI screens with a special annotation @EventListener (ApplicationListener interface is not supported):

@Order(15)
@EventListener
protected void onUserRemove(UserRemovedEvent event) {
    notifications.create()
            .withCaption("User is removed " + event.getUser())
            .show();
}

You can use @Order annotation for event listener ordering.

If an event is UiEvent and fired using the Events bean from UI thread then opened windows and/or frames with such methods will receive the event. Event handling is synchronous. Only UI screens of the current web browser tab opened by the user receive the event.