You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: java/event-handlers/index.md
+96-31Lines changed: 96 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,28 +14,28 @@ uacp: Used as link target from SAP Help Portal at https://help.sap.com/products/
14
14
</style>
15
15
16
16
This section describes how to register event handlers on services. In CAP everything that happens at runtime is an [event](../../about/best-practices#events) that is sent to a [service](../../about/best-practices#services).
17
-
With event handlers the processing of these events can be extended or overridden. Event handlers can be used to handle CRUD events, implement actions and functions and to handle asynchronous events from a messaging service.
17
+
With event handlers, the processing of these events can be extended or overridden. Event handlers can be used to handle CRUD events, implement actions and functions, and to handle asynchronous events from a messaging service.
18
18
19
19
## Introduction to Event Handlers
20
20
21
21
CAP allows you to register event handlers for [events](../../about/best-practices#events) on [services](../../about/best-practices#services). An event handler is simply a Java method.
22
22
Event handlers enable you to add custom business logic to your application by either extending the processing of an event, or by completely overriding its default implementation.
23
23
24
24
::: tip
25
-
Event handlers are a powerful means to extend CAP. Did you know, that most of the built-in features provided by CAP are implemented using event handlers?
25
+
Event handlers are a powerful means to extend CAP. Did you know that most of the built-in features provided by CAP are implemented using event handlers?
26
26
:::
27
27
28
28
Common events are the CRUD events (`CREATE`, `READ`, `UPDATE`, `DELETE`), which are handled by the different kinds of [CQN-based services](../cqn-services/#cdsservices).
29
-
These events are most typically triggered, when an HTTP-based protocol adapter (for example OData V4) executes a CQN statement on an Application Service to fulfill the HTTP request.
29
+
These events are most typically triggered when an HTTP-based protocol adapter (for example OData V4) executes a CQN statement on an Application Service to fulfill the HTTP request.
30
30
The CAP Java SDK provides a lot of built-in event handlers (also known as [Generic Providers](../../guides/providing-services)) that handle CRUD operations out of the box and implement the handling of many CDS annotations.
31
31
Applications most commonly use event handlers on CRUD events to _extend_ the event processing by using the [`Before`](#before) and [`After`](#after) phase.
32
32
33
33
[Actions](../../cds/cdl#actions) and [Functions](../../cds/cdl#actions) that are defined by an Application Service in its model definition are mapped to events as well.
34
34
Therefore, to implement the business logic of an action or function, you need to register event handlers as well.
35
35
Event handlers that implement the core processing of an event should be registered using the [`On`](#on) phase.
36
36
37
-
Events in CAP can have parameters and - in case they are synchronous - a return value. The CAP Java SDK uses [Event Contexts](#eventcontext) to provide a type-safe way to access parameters and return values.
38
-
In the case of CRUD events the corresponding Event Contexts provide for example access to the CQN statement. Event Contexts can be easily obtained in an event handler.
37
+
Events in CAP can have parameters and - in case they're synchronous - a return value. The CAP Java SDK uses [Event Contexts](#eventcontext) to provide a type-safe way to access parameters and return values.
38
+
In the case of CRUD events the corresponding Event Contexts provide, for example, access to the CQN statement. Event Contexts can be easily obtained in an event handler.
39
39
40
40
## Event Phases { #phases}
41
41
@@ -71,7 +71,7 @@ The `On` phase is completed when one of the following conditions applies:
71
71
- A handler throws an exception. In this case, event processing is terminated immediately.
72
72
73
73
In case of synchronous events, if after the `On` phase, no handler completed the event processing, it's considered an error and the event processing is aborted with an exception.
74
-
However when registering an `On` handler for an asynchronous event it is not recommended to complete the event processing, as other handlers might not get notified of the event anymore.
74
+
However, when registering an `On` handler for an asynchronous event it is not recommended to complete the event processing, as other handlers might not get notified of the event anymore.
75
75
In that case CAP ensures to auto-complete the event, once all `On` handlers have been executed.
76
76
77
77
### After { #after}
@@ -108,12 +108,12 @@ srv.emit(context); // process event
108
108
Object result = context.get("result");
109
109
```
110
110
111
-
Using the `get` and `put` methods has several drawbacks: The API is neither type-safe nor is it clear what the correct keys for different event parameters are.
112
-
To solve these issues it is possible to overlay the general Event Context with an event-specific Event Context, which provides typed getters and setters for the parameters of a specific event.
113
-
For each event that the CAP Java SDK provides out-of-the-box (for example the [CRUD events](../cqn-services/application-services#crudevents)) a corresponding Event Context is provided.
111
+
Using the `get` and `put` methods has several drawbacks: The API is not type-safe and it's not clear what the correct keys for different event parameters are.
112
+
To solve these issues, it is possible to overlay the general Event Context with an event-specific Event Context, which provides typed getters and setters for the parameters of a specific event.
113
+
For each event that the CAP Java SDK provides out of thebox (for example the [CRUD events](../cqn-services/application-services#crudevents)) a corresponding Event Context is provided.
114
114
115
115
Let's have a look at an example. The [CdsReadEventContext](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/cds/CdsReadEventContext.html) interface is the `READ` event-specific Event Context.
116
-
As one of the parameters of the `READ` event is a [CqnSelect](../../cds/cqn#select) it provides a `CqnSelect getCqn()` method. The return value of a `READ` event is a [Result](../working-with-cql/query-execution#result).
116
+
As one of the parameters of the `READ` event is a [CqnSelect](../../cds/cqn#select), it provides a `CqnSelect getCqn()` method. The return value of a `READ` event is a [Result](../working-with-cql/query-execution#result).
117
117
The context therefore also provides a `Result getResult()` and a `setResult(Result r)` method. You can use the `as` method provided by the general Event Context to overlay it:
118
118
119
119
```java
@@ -124,7 +124,7 @@ Result result = context.getResult();
124
124
```
125
125
126
126
The getter and setter methods, still operate on the simple get/put API shown in the previous example. They just provide a type-safe layer on top of it.
127
-
The `as` method makes use of Java Proxies behind the scenes. Therefore, an interface definition is all that is required to enable this functionality.
127
+
The `as` method uses Java Proxies behind the scenes. Therefore, an interface definition is all that is required to enable this functionality.
128
128
129
129
::: tip
130
130
Use these event-specific type-safe Event Context interfaces whenever possible.
@@ -219,12 +219,12 @@ public class AdminServiceHandler implements EventHandler {
219
219
:::
220
220
221
221
- The annotation `@Component` instructs Spring Boot to create a bean instance from this class.
222
-
- The `EventHandler` marker interface is required for CAP to identify the class as an event hander class among all beans and scan it for event handler methods.
223
-
- The optional `@ServiceName` annotation can be used to specify the default service, which event handlers are registered on. It is possible to override this value for specific event handler methods.
222
+
- The `EventHandler` marker interface is required for CAP to identify the class as an event handler class among all beans and scan it for event handler methods.
223
+
- The optional `@ServiceName` annotation can be used to specify the default service, which event handlers are registered on. It's possible to override this value for specific event handler methods.
224
224
225
225
::: tip
226
226
The CAP Java SDK Maven Plugin generates interfaces for services in the CDS model. These interfaces provide String constants with the fully qualified name of the service.
227
-
In case the service name is based on the CDS model it is recommended to use these constants with the `@ServiceName` annotation.
227
+
If the service name is based on the CDS model, it's recommended to use these constants with the `@ServiceName` annotation.
228
228
:::
229
229
230
230
It is possible to specify multiple service names. Event handlers are registered on all of these services.
@@ -252,10 +252,10 @@ Each of these annotations can define the following attributes:
252
252
-`serviceType`: The type of services the event handler is registered on, for example, `ApplicationService.class`. Can be used together with `service = "*"` to register an event handler on all services of a certain type.
253
253
254
254
-`event`: The events the event handler is registered on. The event handler is invoked in case any of the events specified matches the current event. Use `*` to match any event.
255
-
It's optional, if the event can be inferred through a[Event Context argument](#contextarguments) in the handler signature.
255
+
It's optional, if the event can be inferred through an[Event Context argument](#contextarguments) in the handler signature.
256
256
257
-
-`entity`: The target entities the event handler is registered on. The event handler is invoked in case any of the entities specified matches the current entity. Use `*` to match any entity.
258
-
It's optional, if the entity can be inferred through a [POJO-based argument](#pojoarguments) in the handler signature. If no value is specified or can be inferred it defaults to `*`.
257
+
-`entity`: The target entities that the event handler is registered on. The event handler is invoked in case any of the entities specified matches the current entity. Use `*` to match any entity.
258
+
It's optional, if the entity can be inferred through a [POJO-based argument](#pojoarguments) in the handler signature. If no value is specified or can be inferred, it defaults to `*`.
259
259
260
260
::: tip
261
261
The interfaces of different service types provide String constants for the events they support (see for example the [CqnService](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/cds/CqnService.html)).
@@ -278,7 +278,7 @@ It is recommended to use these constants with the `event` or `entity` attributes
The most basic signature of an event handler method is `public void process(EventContext context)`. However event-specific Event Context and entity data arguments and certain return values are supported as well and can be freely combined.
281
+
The most basic signature of an event handler method is `public void process(EventContext context)`. However, event-specific Event Context and entity data arguments and return values are supported as well and can be freely combined.
282
282
It is even valid for event handler methods to have no arguments at all. Handler methods don't necessarily have to be public methods. They can also be methods with protected, private, or package visibility.
283
283
284
284
### Event Context Arguments { #contextarguments}
@@ -291,7 +291,7 @@ An event handler can get access to the general `EventContext` by simply declarin
291
291
publicvoid readBooks(EventContext context) { }
292
292
```
293
293
294
-
It is also possible to directly refer to event-specific Event Context interfaces in your arguments. In that case the general Event Context is automatically overlayed with the event-specific one:
294
+
It is also possible to directly refer to event-specific Event Context interfaces in your arguments. In that case the general Event Context is automatically overlaid with the event-specific one:
@@ -326,11 +326,9 @@ public void changeBooks(EventContext context) {
326
326
}
327
327
```
328
328
329
-
330
-
331
329
### Entity Data Arguments { #pojoarguments}
332
330
333
-
When adding business logic to an Application Service event handlers most commonly need to access entity data.
331
+
When adding business logic to an Application Service, event handlers most commonly need to access entity data.
334
332
Entity data can be directly accessed in the event handler method, by using an argument of type `CdsData`:
335
333
336
334
```java
@@ -368,17 +366,17 @@ Entity data arguments only work on [CRUD events](../cqn-services/application-ser
368
366
369
367
The origin from which the entity data is provided depends on the phase of the event processing.
370
368
During the `Before` and `On` phase it is obtained from the CQN statement. The CQN statement contains the entity data that was provided by the service client.
371
-
However during the `After` phase the entity data is obtained from the `Result` object, which is provided as the return value of the event to the service client.
369
+
However, during the `After` phase the entity data is obtained from the `Result` object, which is provided as the return value of the event to the service client.
372
370
Some CQN statements such as for example `CqnSelect`, which is used with `READ` events, don't allow to carry data. In these cases entity data arguments are set to `null`.
373
371
374
-
There are different flavours of entity data arguments. Besides using `List<Books>` it is also possible to use `Stream<Books>`:
372
+
There are different flavors of entity data arguments. Besides using `List<Books>`, it's also possible to use `Stream<Books>`:
It is also possible to use non-collection-based entity arguments, such as `Books`. However if multiple data rows are available at runtime an exception will be thrown in that case:
379
+
It is also possible to use non-collection-based entity arguments, such as `Books`. However, if multiple data rows are available at runtime an exception will be thrown in that case:
@@ -391,6 +389,58 @@ During the `Before` and `On` phase changes affect the data carried by the CQN st
391
389
During the `After` phase changes affect the return value of the event.
392
390
:::
393
391
392
+
### Entity Reference Arguments
393
+
394
+
You can get an entity reference reflecting the reference of the currently processed CQN statement, by declaring a corresponding argument in your method signature.
The CAP Java SDK Maven Plugin can generate query builder interfaces for entities defined in the CDS model. These interfaces allow you to build [type-safe queries](../working-with-cql/query-api#concepts) and can be used in arguments as well:
If an entity reference argument is used, CAP can infer the entity for the event handler registration from the entity reference argument:
409
+
410
+
```java
411
+
@After(event=CqnService.EVENT_UPDATE)
412
+
publicvoid changedBook(Books_ ref) { }
413
+
```
414
+
415
+
::: tip Mapping uses `@CdsName` annotation
416
+
The mapping between a query builder interface and an entity is based on the `@CdsName` annotation of the query builder interface.
417
+
:::
418
+
419
+
Entity data arguments work on all events that operate with a `CqnStatement`. This is the case for all CRUD events and custom bound actions or functions.
420
+
421
+
You can directly use these references to build further queries in your event handlers:
422
+
423
+
```java
424
+
@After(event=CqnService.EVENT_UPDATE)
425
+
publicvoid changedBook(Books_ ref) {
426
+
var select =Select.from(ref).columns(b -> b.title());
427
+
}
428
+
```
429
+
430
+
### Service Arguments { #servicearguments }
431
+
432
+
The CAP Java SDK Maven Plugin can [generate service interfaces](../cqn-services/application-services#trigger-action-or-function) for services defined in the CDS model.
433
+
434
+
To easily access these generated application-specific interfaces, you can declare corresponding arguments in your method signature.
435
+
The same approach works for generic interfaces like `CqnService` or `DraftService`.
436
+
437
+
The service instances that can be provided to the event handler are always the service instances that are processing the event.
The return value of an event can be set by returning a value in an event handler method:
@@ -402,10 +452,10 @@ public Result readBooks(CdsReadEventContext context) {
402
452
}
403
453
```
404
454
405
-
In case an event handler method of the `Before` or `On` phase has a return value it automatically [completes the event processing](#eventcompletion), once it is executed.
455
+
In case an event handler method of the `Before` or `On` phase has a return value it automatically [completes the event processing](#eventcompletion), once it's executed.
406
456
Event handler methods of the `After` phase that have a return value, replace the return value of the event.
407
457
408
-
Only return values that extend `Iterable<? extends Map<String, Object>>` are supported. The `Result` object or a list of entity data (for example `List<Books>`) fulfill this requirement.
458
+
For [CRUD events](../cqn-services/application-services#crudevents) and [draft-specific CRUD events](../fiori-drafts#draftevents), return values that extend `Iterable<? extends Map<String, Object>>` are supported. The `Result` object or a list of entity data (for example `List<Books>`) fulfill this requirement.
409
459
410
460
```java
411
461
@On(entity=Books_.CDS_NAME)
@@ -416,12 +466,27 @@ public List<Books> readBooks(CdsReadEventContext context) {
416
466
}
417
467
```
418
468
419
-
Event handler methods with return values only work on [CRUD events](../cqn-services/application-services#crudevents) of [CQN-based services](../cqn-services/#cdsservices) or the [draft-specific CRUD events](../fiori-drafts#draftevents) provided by Draft Services.
420
-
421
469
::: tip
422
-
To learn how to build your own Result objects, have a look at the [Result Builder API](../cqn-services/application-services#result-builder)
470
+
To learn how to build your own Result objects, have a look at [Result Handling](../cqn-services/application-services#result-handling)
423
471
:::
424
472
473
+
For custom actions or functions, you can directly return the return value of the corresponding action or function in your event handler:
474
+
475
+
Given the following CDS model:
476
+
```cds
477
+
service World {
478
+
function hello() returns String;
479
+
}
480
+
```
481
+
482
+
The event handler can directly return a `String`, which corresponds to the return type of the `hello` function:
483
+
```java
484
+
@On(event="hello")
485
+
publicString hello() {
486
+
return"Hello World";
487
+
}
488
+
```
489
+
425
490
### Ordering of Event Handler Methods
426
491
427
492
You can influence the order in which the event handlers are executed by means of CAP annotation [@HandlerOrder](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/handler/annotations/HandlerOrder.html). It defines the order of handler methods within each phase of events. You may use constants `HandlerOrder.EARLY` or `HandlerOrder.LATE` to place one handler earlier or later relative to the handlers without the annotation. Note that handlers with the same `@HandlerOrder` are executed in a deterministic, but arbitrary sequence.
0 commit comments