diff --git a/src/reference/asciidoc/filter.adoc b/src/reference/asciidoc/filter.adoc index 76948533e1d..2cdbc241f6c 100644 --- a/src/reference/asciidoc/filter.adoc +++ b/src/reference/asciidoc/filter.adoc @@ -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 + .filter((payload) -> !"junk".equals(payload)); +} +---- +[source, kotlin, role="secondary"] +.Kotlin DSL +---- +@Bean +fun someFlow() = + integrationFlow { + filter { 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 `` 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: diff --git a/src/reference/asciidoc/router.adoc b/src/reference/asciidoc/router.adoc index 94889a986f7..bcaff178bea 100644 --- a/src/reference/asciidoc/router.adoc +++ b/src/reference/asciidoc/router.adoc @@ -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 ``, it can be configured without any `` 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"] ---- @@ -775,6 +815,7 @@ The following example shows a sample configuration for `ErrorMessageExceptionTyp ---- +==== [[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`. diff --git a/src/reference/asciidoc/service-activator.adoc b/src/reference/asciidoc/service-activator.adoc index 551b2d39441..d8f84509e51 100644 --- a/src/reference/asciidoc/service-activator.adoc +++ b/src/reference/asciidoc/service-activator.adoc @@ -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. diff --git a/src/reference/asciidoc/splitter.adoc b/src/reference/asciidoc/splitter.adoc index 3059979ca04..17d3f53ebd1 100644 --- a/src/reference/asciidoc/splitter.adoc +++ b/src/reference/asciidoc/splitter.adoc @@ -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> { 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 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>>. diff --git a/src/reference/asciidoc/transformer.adoc b/src/reference/asciidoc/transformer.adoc index dff9c18175a..9266e1fda3f 100644 --- a/src/reference/asciidoc/transformer.adoc +++ b/src/reference/asciidoc/transformer.adoc @@ -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 ---- ---- @@ -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 ---- + 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 ---- @@ -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 ---- ---- @@ -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 ---- ---- ==== -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 ---- - ---- +==== 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 ---- @@ -318,10 +520,7 @@ The following pair of examples show how to declare them in XML: [source,xml] ---- ----- -[source,xml] ----- ---- @@ -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 ----