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: docs/src/main/asciidoc/spring-cloud-stream.adoc
+74Lines changed: 74 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -1485,6 +1485,80 @@ NOTE: If you need to support dynamic destinations with multiple binder types, us
1485
1485
1486
1486
Also, please see <<Using StreamBridge>> section to see how yet another option (StreamBridge) can be utilized for similar cases.
1487
1487
1488
+
=== Post processing (after sending message)
1489
+
1490
+
Once function is invoked, its result is sent by the framework to a target destination which effectively completes function invocation cycle.
1491
+
1492
+
However such cycle may not be fully complete from the business standpoint until some additional tasks are performed *after* completion of this cycle.
1493
+
While this could be accomplished with a simple combination of `Consumer` and `StreamBridge` as described in this
1494
+
https://stackoverflow.com/questions/75917883/post-processing-after-spring-cloud-stream-function[Stack Overflow post], since version 4.0.3 the framework
1495
+
provides a more idiomatic approach to solve this issue via `PostProcessingFunction` provided by Spring Cloud Function project.
1496
+
The `PostProcessingFunction` is a special semi-marker function which contains one additional method `postProcess(Message>)` designed
1497
+
to provide a place for implementing such post processing task.
public interface PostProcessingFunction<I, O> extends Function<I, O> {
1503
+
default void postProcess(Message<O> result) {
1504
+
}
1505
+
}
1506
+
----
1507
+
1508
+
So, now you have two options.
1509
+
1510
+
Option 1: You can implement your function as `PostProcessingFunction` and also include the additional post processing behavior by implementing its `postProcess(Message>)` method.
1511
+
1512
+
----
1513
+
private static class Uppercase implements PostProcessingFunction<String, String> {
1514
+
1515
+
@Override
1516
+
public String apply(String input) {
1517
+
return input.toUpperCase();
1518
+
}
1519
+
1520
+
@Override
1521
+
public void postProcess(Message<String> result) {
1522
+
System.out.println("Function Uppercase has been successfully invoked and its result successfully sent to target destination");
1523
+
}
1524
+
}
1525
+
. . .
1526
+
@Bean
1527
+
public Function<String, String> uppercase() {
1528
+
return new Uppercase();
1529
+
}
1530
+
----
1531
+
1532
+
Option 2: If you already have an existing function and don't want to change its implementation or want to keep your function as POJO, you can simply implement only `postProcess(Message>)` method and compose this new post processing function with your other function.
1533
+
----
1534
+
private static class Logger implements PostProcessingFunction<?, String> {
1535
+
1536
+
@Override
1537
+
public void postProcess(Message<String> result) {
1538
+
System.out.println("Function has been successfully invoked and its result successfully sent to target destination");
1539
+
}
1540
+
}
1541
+
. . .
1542
+
@Bean
1543
+
public Function<String, String> uppercase() {
1544
+
return v -> v.toUpperCase();
1545
+
}
1546
+
@Bean
1547
+
public Function<String, String> logger() {
1548
+
return new Logger();
1549
+
}
1550
+
. . .
1551
+
// and then have your function definition as such `uppercase|logger`
1552
+
----
1553
+
1554
+
NOTE:
1555
+
In case of function composition only the last instance of `PostProcessingFunction` (if present) will take effect. For example, let's say you have the
1556
+
following function definition - `foo|bar|baz` and both `foo` and `baz` are instances of `PostProcessingFunction`. Only `baz.postProcess(Message>)` will be invoked.
1557
+
If `baz` is not an instance of `PostProcessingFunction`, then no post processing functionality will be performed.
1558
+
1559
+
One may argue that you can easily do that via function composition by simply composing a post-processor as just another `Function`. That is indeed a possibility however
1560
+
the post processing functionality in this case will be invoked right after invocation of the previous function and before the message is sent to a target destination
1561
+
which is before the function invocation cycle is complete.
0 commit comments