GH-8626: Provide cleaner transform() DSL (#8653)

* GH-8626: Provide cleaner `transform()` DSL

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

* Add missed `transform(String beanName, @Nullable String methodName)` API
* Introduce a `TransformerSpec` to expose a strict API to configure
transformer variants.
* Introduce `transformWith(Consumer<TransformerSpec>)` as a single point
of all possible transformer and its endpoint options
* Deprecate those `IntegrationFlowDefinition.transform()` variants
which are harder to configure as several lambda arguments

This change will make Kotlin & Groovy DSL more readable and straightforward

* * Use new `transformWith()` in tests where a deprecated API still used
* Add JavaDocs to `TransformerSpec`
* Fix generic types for `BaseIntegrationFlowDefinition.transformWith()` -
they make sense exactly on the `TransformerSpec.transformer()` only
* Apply `transformWith()` for Groovy DSL
* Introduce a new `ClassUtils.isLambda(Object candidate)`
and add a check for Groovy `Closure`
* Fix `GroovyIntegrationFlowDefinition.createConfigurerIfAny()` to propagate a `Consumer` argument
down to the `Closure`

* * Rename `TransformerSpec -> TransformerEndpointSpec` for better context
meaning of the class
* Introduce `KotlinTransformerEndpointSpec` as an extension of the `TransformerEndpointSpec`
to have an `inline fun <reified P> transformer(crossinline function: (P) -> Any)`
for Kotlin style
* Add `KotlinIntegrationFlowDefinition.transformWith(KotlinTransformerEndpointSpec)`
* Deprecate Kotlin methods which are covered by the mentioned `transformWith()`
* Fix tests to use new API
* Mentioned the change in the doc
This commit is contained in:
Artem Bilan
2023-06-26 12:01:13 -04:00
committed by GitHub
parent ceb3daec8e
commit 070c1c6e60
20 changed files with 593 additions and 61 deletions

View File

@@ -64,6 +64,7 @@ import org.springframework.integration.dsl.MessageChannels;
import org.springframework.integration.dsl.PollerSpec;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.dsl.QueueChannelSpec;
import org.springframework.integration.dsl.TransformerEndpointSpec;
import org.springframework.integration.dsl.Transformers;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.endpoint.EventDrivenConsumer;
@@ -671,17 +672,24 @@ public class IntegrationFlowTests {
.fixedSubscriberChannel()
.<String, Integer>transform(Integer::parseInt)
.transform(Foo::new)
.transform(new PayloadSerializingTransformer(),
c -> c.autoStartup(false).id("payloadSerializingTransformer"))
.transformWith(this::payloadSerializingTransformer)
.channel(MessageChannels.queue(new SimpleMessageStore(), "fooQueue"))
.transform(Transformers.deserializer(Foo.class.getName()))
.<Foo, Integer>transform(f -> f.value)
.filter("true", e -> e.id("expressionFilter"))
.channel(publishSubscribeChannel())
.transform((Integer p) -> p * 2, c -> c.advice(this.expressionAdvice()))
.transformWith(t -> t
.transformer((Integer p) -> p * 2)
.advice(expressionAdvice()))
.get();
}
private void payloadSerializingTransformer(TransformerEndpointSpec spec) {
spec.transformer(new PayloadSerializingTransformer())
.autoStartup(false)
.id("payloadSerializingTransformer");
}
@Bean
public MessageChannel publishSubscribeChannel() {
return new PublishSubscribeChannel();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -190,7 +190,8 @@ public class ManualFlowTests {
IntegrationFlow myFlow = f -> f
.<String, String>transform(String::toUpperCase)
.channel(MessageChannels.queue())
.transform("Hello, "::concat, e -> e
.transformWith(t -> t
.transformer("Hello, "::concat)
.poller(p -> p
.fixedDelay(10)
.maxMessagesPerPoll(1)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -262,8 +262,10 @@ public class ReactiveStreamsTests {
return IntegrationFlow
.from("inputChannel")
.split(s -> s.delimiters(","))
.<String, Integer>transform(Integer::parseInt,
e -> e.reactive(flux -> flux.publishOn(Schedulers.parallel())).id("reactiveTransformer"))
.transformWith(t -> t
.<String, Integer>transformer(Integer::parseInt)
.reactive(flux -> flux.publishOn(Schedulers.parallel()))
.id("reactiveTransformer"))
.channel(MessageChannels.queue())
.log()
.toReactivePublisher();

View File

@@ -460,10 +460,11 @@ public class TransformerTests {
@Bean
public IntegrationFlow transformFlowWithError() {
return f -> f
.transform(p -> {
throw new RuntimeException("intentional");
},
e -> e.advice(expressionAdvice()))
.transformWith((t) ->
t.transformer(p -> {
throw new RuntimeException("intentional");
})
.advice(expressionAdvice()))
.log();
}

View File

@@ -161,7 +161,10 @@ class KotlinDslTests {
val integrationFlow =
integrationFlow(publisher) {
transform<Message<Int>>({ it.payload * 2 }) { id("foo") }
transformWith {
transformer<Message<Int>> { it.payload * 2 }
id("foo")
}
channel(fluxChannel)
}
@@ -249,7 +252,10 @@ class KotlinDslTests {
@Bean
fun functionFlow() =
integrationFlow<Function<ByteArray, String>>({ beanName("functionGateway") }) {
transform(Transformers.objectToString()) { id("objectToStringTransformer") }
transformWith {
transformer(Transformers.objectToString())
id("objectToStringTransformer")
}
transform<String> { it.uppercase() }
split<Message<*>> { it.payload }
split<String>({ it }) { id("splitterEndpoint") }
@@ -292,7 +298,10 @@ class KotlinDslTests {
fun fixedSubscriberFlow() =
integrationFlow("fixedSubscriberInput", true) {
log<Any>(LoggingHandler.Level.WARN) { it.payload }
transform("payload") { id("spelTransformer") }
transformWith {
expression("payload")
id("spelTransformer")
}
}
@Bean