GH-8583: Add Java & DSL samples into docs (#8608)

* GH-8583: Add Java & DSL samples into docs

Fixes https://github.com/spring-projects/spring-integration/issues/8583

When users jump into docs first thing these days they expect to see a Java DSL sample.

* Fix language in Docs

Co-authored-by: Gary Russell <grussell@vmware.com>

* * Improve `router.adoc` for DSL samples
* Add links to DSL chapters

---------

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2023-05-01 16:49:16 -04:00
committed by GitHub
parent 212bd46d65
commit 33d13a4649
5 changed files with 423 additions and 27 deletions

View File

@@ -31,11 +31,54 @@ MessageFilter filter = new MessageFilter(someSelector);
----
====
In combination with the namespace and SpEL, you can configure powerful filters with very little Java code.
[[filter-dsl]]
==== Configuring a Filter with Java, Groovy and Kotlin DSLs
The `IntegrationFlowBuilder` provided by the Java DSL (which is also used as a base for the Groovy and Kotlin DSLs) provides a number of overloaded methods for the `filter()` operator.
The `MessageSelector` abstraction mentioned above can be used as a Lambda in a `filter()` definition:
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return f -> f
.<String>filter((payload) -> !"junk".equals(payload));
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow {
filter<String> { it != "junk" }
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow {
filter String, { it != 'junk' }
}
}
----
====
See more information about DSLs in the respective chapters:
* <<./dsl.adoc#java-dsl,Java DSL>>
* <<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>>
* <<./groovy-dsl.adoc#groovy-dsl,Groovy DSL>>
[[filter-xml]]
==== Configuring a Filter with XML
In combination with the namespace and SpEL, you can configure powerful filters with very little Java code.
You can use the `<filter>` element is used to create a message-selecting endpoint.
In addition to `input-channel` and `output-channel` attributes, it requires a `ref` attribute.
The `ref` can point to a `MessageSelector` implementation, as the following example shows:

View File

@@ -739,7 +739,7 @@ It is a "`registering an interest in something that is part of the message`" use
Because of the runtime management operation for the `<recipient-list-router>`, it can be configured without any `<recipient>` from the start.
In this case, the behavior of `RecipientListRouter` is the same when there is no one matching recipient for the message.
If `defaultOutputChannel` is configured, the message is sent there.
Otherwise the `MessageDeliveryException` is thrown.
Otherwise, the `MessageDeliveryException` is thrown.
[[router-implementations-xpath-router]]
===== XPath Router
@@ -762,7 +762,47 @@ NOTE: Since version 4.3 the `ErrorMessageExceptionTypeRouter` loads all mapping
The following example shows a sample configuration for `ErrorMessageExceptionTypeRouter`:
[source,xml]
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return f -> f
.routeByException(r -> r
.channelMapping(IllegalArgumentException.class, "illegalChannel")
.channelMapping(NullPointerException.class, "npeChannel")
.defaultOutputChannel("defaultChannel"));
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow {
routeByException {
channelMapping(IllegalArgumentException::class.java, "illegalChannel")
channelMapping(NullPointerException::class.java, "npeChannel")
defaultOutputChannel("defaultChannel")
}
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow {
routeByException {
channelMapping IllegalArgumentException, 'illegalChannel'
channelMapping NullPointerException, 'npeChannel'
defaultOutputChannel 'defaultChannel'
}
}
}
----
[source, xml, role="secondary"]
----
<int:exception-type-router input-channel="inputChannel"
default-output-channel="defaultChannel">
@@ -775,6 +815,7 @@ The following example shows a sample configuration for `ErrorMessageExceptionTyp
<int:channel id="illegalChannel" />
<int:channel id="npeChannel" />
----
====
[[router-namespace]]
==== Configuring a Generic Router
@@ -1115,7 +1156,7 @@ That basically involves a bean lookup for the provided name.
Now all messages that contain the header-value pair as `testHeader=kermit` are going to be routed to a `MessageChannel` whose bean name (its `id`) is 'kermit'.
But what if you want to route these messages to the 'simpson' channel? Obviously changing a static configuration works, but doing so also requires bringing your system down.
However, if you have had an access to the channel identifier map, you could introduce a new mapping where the header-value pair is now `kermit=simpson`, thus letting the second step treat 'kermit' as a channel identifier while resolving it to 'simpson' as the channel name.
However, if you have had access to the channel identifier map, you could introduce a new mapping where the header-value pair is now `kermit=simpson`, thus letting the second step treat 'kermit' as a channel identifier while resolving it to 'simpson' as the channel name.
The same obviously applies for `PayloadTypeRouter`, where you can now remap or remove a particular payload type mapping.
In fact, it applies to every other router, including expression-based routers, since their computed values now have a chance to go through the second step to be resolved to the actual `channel name`.

View File

@@ -10,7 +10,67 @@ As with most of the configuration options described here, the same behavior actu
[[service-activator-namespace]]
==== Configuring Service Activator
To create a service activator, use the 'service-activator' element with the 'input-channel' and 'ref' attributes, as the following example shows:
With Java & Annotation configuration, it is sufficient to mark the respective service method with the `@ServiceActivator` annotation - and the framework calls it when messages are consumed from an input channel:
====
[source,java]
----
public class SomeService {
@ServiceActivator(inputChannel = "exampleChannel")
public void exampleHandler(SomeData payload) {
...
}
}
----
====
See more information in the <<./configuration.adoc#annotations, Annotation Support>>.
For Java, Groovy or Kotlin DSLs, the `.handle()` operator of an `IntegrationFlow` represents a service activator:
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("exampleChannel")
.handle(someService, "exampleHandler")
.get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow("exampleChannel") {
handle(someService, "exampleHandler")
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow 'exampleChannel',
{
handle someService, 'exampleHandler'
}
}
----
====
See more information about the DSLs in the respective chapters:
* <<./dsl.adoc#java-dsl,Java DSL>>
* <<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>>
* <<./groovy-dsl.adoc#groovy-dsl,Groovy DSL>>
To create a service activator when using XML configuration, use the 'service-activator' element with the 'input-channel' and 'ref' attributes, as the following example shows:
====
[source,xml]
@@ -59,7 +119,7 @@ If it can be resolved, the message is sent there.
If the request message does not have a `replyChannel` header and the `reply` object is a `Message`, its `replyChannel` header is consulted for a target destination.
This is the technique used for request-reply messaging in Spring Integration, and it is also an example of the return address pattern.
If your method returns a result and you want to discard it and end the flow, you should configure the `output-channel` to send to a `NullChannel`.
If your method returns a result, and you want to discard it and end the flow, you should configure the `output-channel` to send to a `NullChannel`.
For convenience, the framework registers one with the name, `nullChannel`.
See <<./channel.adoc#channel-special-channels,Special Channels>> for more information.

View File

@@ -69,6 +69,46 @@ Starting with version 5.2, the splitter supports a `discardChannel` option for s
In this case there is just no item to iterate for sending to the `outputChannel`.
The `null` splitting result remains as an end of flow indicator.
==== Configuring a Splitter with Java, Groovy and Kotlin DSLs
An example of simple splitter based on a `Message` and its iterable payload with DSL configuration:
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return f -> f.split(Message.class, Message::getPayload);
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow {
split<Message<*>> { it.payload }
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow {
split Message<?>, { it.payload }
}
}
----
====
See more information about the DSLs in the respective chapters:
* <<./dsl.adoc#java-dsl,Java DSL>>
* <<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>>
* <<./groovy-dsl.adoc#groovy-dsl,Groovy DSL>>
==== Configuring a Splitter with XML
A splitter can be configured through XML as follows:
@@ -142,4 +182,4 @@ List<LineItem> extractItems(Order order) {
----
====
See also <<./handler-advice.adoc#advising-with-annotations,Advising Endpoints Using Annotations>>, <<./dsl.adoc#java-dsl-splitters,Splitters>> and <<./file.adoc#file-splitter, File Splitter>>.
See also <<./handler-advice.adoc#advising-with-annotations,Advising Endpoints Using Annotations>> and <<./file.adoc#file-splitter, File Splitter>>.

View File

@@ -14,6 +14,71 @@ NOTE: For the sake of maximizing flexibility, Spring does not require XML-based
Nevertheless, the framework does provide some convenient transformers for dealing with XML-based payloads if that is indeed the right choice for your application.
For more information on those transformers, see <<./xml.adoc#xml,XML Support - Dealing with XML Payloads>>.
==== Configuring a Transformer with Java and other DSLs
For simple Java & Annotation configuration, the Spring bean POJO method must be marked with a `@Transformer` annotation and the framework calls it when messages are consumed from an input channel:
====
[source,java]
----
public class SomeService {
@Transfomer(inputChannel = "transformChannel", outputChannel = "nextServiceChannel")
public OutputData exampleTransformer(InputData payload) {
...
}
}
----
====
See more information in the <<./configuration.adoc#annotations, Annotation Support>>.
For Java, Groovy or Kotlin DSLs, the `.transform()` operator of an `IntegrationFlow` represents a transformer endpoint:
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("transformChannel")
.transform(someService, "exampleTransformer")
.channel("nextServiceChannel")
.get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow("transformChannel") {
transform(someService, "exampleTransformer")
channel("nextServiceChannel")
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow 'transformChannel',
{
transform someService, 'exampleTransformer'
channel 'nextServiceChannel'
}
}
----
====
See more information about the DSLs in the respective chapters:
* <<./dsl.adoc#java-dsl,Java DSL>>
* <<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>>
* <<./groovy-dsl.adoc#groovy-dsl,Groovy DSL>>
[[transformer-namespace]]
==== Configuring a Transformer with XML
@@ -83,12 +148,47 @@ Spring Integration provides a few transformer implementations.
===== Object-to-String Transformer
Because it is fairly common to use the `toString()` representation of an `Object`, Spring Integration provides an `ObjectToStringTransformer` whose output is a `Message` with a String `payload`.
Because it is fairly common to use the `toString()` representation of an `Object`, Spring Integration provides an `ObjectToStringTransformer` (see also the `Transformers` factory) where the output is a `Message` with a String `payload`.
That `String` is the result of invoking the `toString()` operation on the inbound Message's payload.
The following example shows how to declare an instance of the object-to-string transformer:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("in")
.transform(Transformers.objectToString())
.channel("out")
.get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun someFlow() =
integrationFlow("in") {
transform(Transformers.objectToString())
channel("out")
}
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
someFlow() {
integrationFlow 'in',
{
transform Transformers.objectToString()
channel 'out'
}
}
----
[source, xml, role="secondary"]
.XML
----
<int:object-to-string-transformer input-channel="in" output-channel="out"/>
----
@@ -102,8 +202,6 @@ Otherwise, you can provide a custom POJO-based transformer by using the generic
TIP: When debugging, this transformer is not typically necessary, since the `logging-channel-adapter` is capable of logging the message payload.
See <<./channel.adoc#channel-wiretap,Wire Tap>> for more detail.
[NOTE]
====
The object-to-string transformer is very simple.
It invokes `toString()` on the inbound payload.
Since Spring Integration 3.0, there are two exceptions to this rule:
@@ -114,19 +212,50 @@ The `charset` can be modified by supplying the charset attribute on the transfor
For more sophistication (such as selection of the charset dynamically, at runtime), you can use a SpEL expression-based transformer instead, as the following example shows:
[source,xml]
====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("in")
.transform("new String(payload, headers['myCharset']")
.channel("out")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:transformer input-channel="in" output-channel="out"
expression="new java.lang.String(payload, headers['myCharset']" />
expression="new String(payload, headers['myCharset']" />
----
====
If you need to serialize an `Object` to a byte array or deserialize a byte array back into an `Object`, Spring Integration provides symmetrical serialization transformers.
These use standard Java serialization by default, but you can provide an implementation of Spring `Serializer` or `Deserializer` strategies by using the `serializer` and `deserializer` attributes, respectively.
See also the `Transformers` factory class.
The following example shows to use Spring's serializer and deserializer:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("objectsIn")
.transform(Transformers.serializer())
.channel("bytesOut")
.channel("bytesIn")
.transform(Transformers.deserializer("com.mycom.*", "com.yourcom.*"))
.channel("objectsOut")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
@@ -226,10 +355,23 @@ The two classes in the preceding example are transformed to the following `Map`:
----
====
To configure these transformers, Spring Integration provides namespace support for Object-to-Map, as the following example shows:
To configure these transformers, Spring Integration provides respective XML component and Java DSL factory:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("directInput")
.transform(Transformers.toMap())
.channel("output")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:object-to-map-transformer input-channel="directInput" output-channel="output"/>
----
@@ -238,16 +380,42 @@ To configure these transformers, Spring Integration provides namespace support f
You can also set the `flatten` attribute to false, as follows:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("directInput")
.transform(Transformers.toMap(false))
.channel("output")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:object-to-map-transformer input-channel="directInput" output-channel="output" flatten="false"/>
----
====
Spring Integration provides namespace support for Map-to-Object, as the following example shows:
Spring Integration provides XML namespace support for Map-to-Object and the Java DSL factory has the `fromMap()` method, as the following example shows:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("input")
.transform(Transformers.fromMap(org.something.Person.class))
.channel("output")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:map-to-object-transformer input-channel="input"
output-channel="output"
@@ -256,14 +424,35 @@ Spring Integration provides namespace support for Map-to-Object, as the followin
====
Alternatively, you could use a `ref` attribute and a prototype-scoped bean, as the following example shows:
[source,xml]
====
[source, java, role="primary"]
.Java DSL
----
@Bean
IntegrationFlow someFlow() {
return IntegrationFlow
.from("inputA")
.transform(Transformers.fromMap("person"))
.channel("outputA")
.get();
}
@Bean
@Scope("prototype")
Person person() {
return new Person();
}
----
[source, xml, role="secondary"]
.XML
----
<int:map-to-object-transformer input-channel="inputA"
output-channel="outputA"
ref="person"/>
<bean id="person" class="org.something.Person" scope="prototype"/>
----
====
NOTE: The 'ref' and 'type' attributes are mutually exclusive.
Also, if you use the 'ref' attribute, you must point to a 'prototype' scoped bean.
@@ -280,7 +469,20 @@ The `StreamTransformer` transforms `InputStream` payloads to a `byte[]`( or a `S
The following example shows how to use the `stream-transformer` element in XML:
====
[source, xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("input")
.transform(Transformers.fromStream("UTF-8"))
.channel("output")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:stream-transformer input-channel="directInput" output-channel="output"/> <!-- byte[] -->
@@ -318,10 +520,7 @@ The following pair of examples show how to declare them in XML:
[source,xml]
----
<int:object-to-json-transformer input-channel="objectMapperInput"/>
----
[source,xml]
----
<int:json-to-object-transformer input-channel="objectMapperInput"
type="foo.MyDomainObject"/>
----
@@ -364,7 +563,7 @@ public class ObjectMapperFactory {
----
====
The following example shows how to do the same thing in XML
The following example shows how to do the same thing in XML:
====
[source,xml]
@@ -546,7 +745,20 @@ The latter is discussed in <<./content-enrichment.adoc#header-enricher,Header En
The following example defines a header filter:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow someFlow() {
return IntegrationFlow
.from("inputChannel")
.headerFilter("lastName", "state")
.channel("outputChannel")
.get();
}
----
[source, xml, role="secondary"]
.XML
----
<int:header-filter input-channel="inputChannel"
output-channel="outputChannel" header-names="lastName, state"/>