# 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
- BeforeRead
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
orafter_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);