# 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

# RequestEvents

RequestEvents are structured hierarchically:

  • 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.

BeforeRead and AfterRead

Instead of using three hooks

  • before_get_single
  • before_get_multiple
  • before_search
    or
  • after_get_single
  • after_get_multiple
  • after_search

they can be implemented in single event BeforeRead or AfterRead.

Further information: Methods for a RequestEvent

# 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));
  }
}

# Methods for a RequestEvent

# Method to Get Values from a RequestEvent

To get values from a RequestEvent, you can use the following function:

var optionalValues = requestEvent.values();

This method returns the value map of the corresponding event as an Optional. If the request does not support values, it returns {@link Optional}.empty. Otherwise, it returns {@link Optional} filled with either an empty or filled {@link Collection} from type Map<String, Object>.

# Method to Get the Value of a Field from the Map

To get the value of a field from the map returned by requestEvent.values();, you can use the following method:

getValue(@NonNull Map<String, Object> values, @NonNull IFieldName fieldName)

Example:

var map = new HashMap<String, Object>(
    Map.of(UserBeanFieldName.FIRSTNAME.toString(), new CEVarchar("user1")));
var result = CETypeUtil.getValue(map, UserBeanFieldName.FIRSTNAME);

# Method to Get the CEType of a field from the Map

To get the CEType of a field from the map returned by requestEvent.values();, you can use the following method:

getCEType(@NonNull Map<String, Object> values, @NonNull IFieldName fieldName)

Example:

var user1 = new CEVarchar("user1");
var map = new HashMap<String, Object>(Map.of(UserBeanFieldName.FIRSTNAME.toString(), user1));
var result = CETypeUtil.getCEType(map, UserBeanFieldName.FIRSTNAME);
Request missing documentation