Skip to content

Commit 0b5ae25

Browse files
committed
GH-2707 Add PostProcessing documentation
Resolves #2707
1 parent 3c29149 commit 0b5ae25

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

docs/src/main/asciidoc/spring-cloud-stream.adoc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,80 @@ NOTE: If you need to support dynamic destinations with multiple binder types, us
14851485

14861486
Also, please see <<Using StreamBridge>> section to see how yet another option (StreamBridge) can be utilized for similar cases.
14871487

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.
1498+
1499+
----
1500+
package org.springframework.cloud.function.context
1501+
. . .
1502+
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.
14881562

14891563
[[spring-cloud-stream-overview-error-handling]]
14901564
=== Error Handling

0 commit comments

Comments
 (0)