# Core Engine Events

The CeEvent System provides a way to execute additional logic on certain events. This allows 4allportal apps to perform the required computational steps during the processing of requests. The event listeners are meant to be a more convenient replacement for the hook system.

# Event Listeners

In order to create an Event Listener it is sufficient to extend the CeEventListener class. The CeEventListener is typed with the Event it is supposed to listen to. The following code shows a listener which increases a counter each time the AfterRead event is published by the core engine.


@Component
public class AfterReadCounter extends CeEventListener<AfterRead> {

  private int counter = 0;

  @Override
  protected void execute(AfterRead event) {
    counter++;
  }
}

The code shows a complete and working example. There is no need for a configuration and the listener does NOT need to be registered. This is done by the core engine which uses the Spring IoC container to find all CeEventListener. Thus, each Event Listener needs to be a Spring Managed Bean (e.g. @Component).

Event Listeners are executed synchronously in an order which can be defined using the @Order annotation of the spring framework.

# Configuration

During the Initialization of the InitializeCoreEngineService, a single config for ALL CeEventListener is created and stored in the CeConfig. This config contains two maps. One maps the name of a listener to a boolean which determines whether the listener is activated. And the other maps the name of a listener to its order value. By default, all listeners found are activated and provided with the lowest precedence/order.

Even though it is possible to alter this config programmatically it is NOT recommended. If changes need to be made for a certain system this can be done by placing an event_config.xml in the custom/global/plugins/event/ directory.

The following shows the content of a event_config.xml which deactivates the TestListener and sets the order value of the ProductionListener to 99.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<eventListenerConfig>
  <active>
    <entry>
      <key>TestListener</key>
      <value>false</value>
    </entry>
  </active>
  <order>
    <entry>
      <key>ProductionListener</key>
      <value>99</value>
    </entry>
  </order>
</eventListenerConfig>

Notice that it is not necessary to define values for all existing listeners. The listener which do not occur in the config fall back to the default values. Thus, the config above has the same effect as the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<eventListenerConfig>
  <active>
    <entry>
      <key>TestListener</key>
      <value>false</value>
    </entry>
    <entry>
      <key>ProductionListener</key>
      <value>true</value>
    </entry>
  </active>
  <order>
    <entry>
      <key>TestListener</key>
      <value>2147483647</value>
    </entry>
    <entry>
      <key>ProductionListener</key>
      <value>99</value>
    </entry>
  </order>
</eventListenerConfig>

The keys used by the maps are the simple names of the classes. Therefore, the names of the listeners need to be unique over the whole system.

The values of the xml file overwrite the default values as well as the order defined with the @Order annotation.

# Events

# Requests Events

Request Events are hierarchically structured:

  • RequestEvent
    • BeforeRead
      • BeforeGetEvent
      • BeforeSearchEvent
    • BeforeWrite
      • BeforeSetEvent
      • BeforeDeleteEvent
    • AfterWrite
      • AfterDeleteEvent
      • AfterSetEvent
    • AfterRead
      • AfterGetEvent
      • AfterSearchEvent

A listener that is typed with the parent class automatically obtains also the events of the children. Thus, a listener which listens to AfterRead Events is executed on all AfterGetEvent and on all AfterSearchEvent.

In opposite to the hooks the events are not bound to a module. This makes it easier to deal with requests which make use of the relation feature.

# AfterCreateSessionEvent

This event allows to change the session for a user after the session is created. This event is designed to replace the after_session_create hook.

Example

import com.cm4ap.ce.ce_event.events.AfterCreateSessionEvent;
import com.cm4ap.ce.ce_event.service.CeEventListener;
import com.cm4ap.ce.model.ModelLocator;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(20)
public class TestAfterCreateSessionEventListener extends CeEventListener<AfterCreateSessionEvent> {

  private final ModelLocator modelLocator;

  public TestAfterCreateSessionEventListener(ModelLocator modelLocator) {
    this.modelLocator = modelLocator;
  }

  @Override
  protected void execute(AfterCreateSessionEvent event) {
    event.getUserPermission();
  }
}

The event contains the UserPermission object that just was created. Only this object may be altered. It is not recommended accessing the session container directly because the corresponding object might be null or outdated.

# Create new ValueOption via CeLoad event

There is a possibility to generate ValueOption at runtime. If property noCache is set to true, it must not be cached.

The generated ValueOption can be overwritten by configured ones.

Example

package com.cm4ap.ce.view.objectrenderer;

import com.cm4ap.ce.event.CeLoad;
import com.cm4ap.ce.model.ModelLocator;
import com.cm4ap.ce.service.IValueOptionService;
import java.util.List;
import org.springframework.context.event.EventListener;

@Service
public class RegisterValueOptions {

  private final ModelLocator modelLocator;
  private final IValueOptionService valueOptionService;

  public ObjectRendererValueOptionService(ModelLocator modelLocator,
      IValueOptionService valueOptionService) {
    this.modelLocator = modelLocator;
    this.valueOptionService = valueOptionService;
  }

  @EventListener
  public void registerTestValueOption(CeLoad event) {
    var type = "test_value_option";
    valueOptionService.registerValueOption(type,
        () -> valueOptionService.generateValueOption(type, List.of("Value1", "Value2"))
            .noCache(true));
  }
}
Request missing documentation