Fix DSL sample in the doc

* Improve JavaDocs for `BaseIntegrationFlowDefinition` with renaming `payloadType`
param to `expectedType` since it can also be as a `Message.class`, not only type
for the payload.
* Add JavaDoc for `LambdaMessageProcessor` ctor
This commit is contained in:
Artem Bilan
2021-10-19 14:19:58 -04:00
parent 7e5fef9e45
commit d61df01acf
3 changed files with 64 additions and 56 deletions

View File

@@ -668,9 +668,9 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
/**
* Populate the {@link MessageTransformingHandler} instance for the provided
* {@link GenericTransformer} for the specific {@code payloadType} to convert at
* {@link GenericTransformer} for the specific {@code expectedType} to convert at
* runtime.
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the transformer.
* Conversion to this type will be attempted, if necessary.
* @param genericTransformer the {@link GenericTransformer} to populate.
@@ -680,14 +680,14 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @see MethodInvokingTransformer
* @see LambdaMessageProcessor
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer) {
return transform(payloadType, genericTransformer, null);
public <P, T> B transform(Class<P> expectedType, GenericTransformer<P, T> genericTransformer) {
return transform(expectedType, genericTransformer, null);
}
/**
* Populate the {@link MessageTransformingHandler} instance
* for the provided {@code payloadType} to convert at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* In addition, accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param payloadType the {@link Class} for expected payload type.
* @param endpointConfigurer the {@link Consumer} to provide integration endpoint options.
* @param <P> the payload type - 'transform to'.
@@ -706,9 +706,9 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
/**
* Populate the {@link MessageTransformingHandler} instance for the provided {@link GenericTransformer}
* for the specific {@code payloadType} to convert at runtime.
* for the specific {@code expectedType} to convert at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the transformer.
* Conversion to this type will be attempted, if necessary.
* @param genericTransformer the {@link GenericTransformer} to populate.
@@ -720,13 +720,13 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @see LambdaMessageProcessor
* @see GenericEndpointSpec
*/
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer,
public <P, T> B transform(Class<P> expectedType, GenericTransformer<P, T> genericTransformer,
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
Assert.notNull(genericTransformer, "'genericTransformer' must not be null");
Transformer transformer = genericTransformer instanceof Transformer ? (Transformer) genericTransformer :
(ClassUtils.isLambda(genericTransformer.getClass())
? new MethodInvokingTransformer(new LambdaMessageProcessor(genericTransformer, payloadType))
? new MethodInvokingTransformer(new LambdaMessageProcessor(genericTransformer, expectedType))
: new MethodInvokingTransformer(genericTransformer, ClassUtils.TRANSFORMER_TRANSFORM_METHOD));
return addComponent(transformer)
.handle(new MessageTransformingHandler(transformer), endpointConfigurer);
@@ -840,13 +840,13 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* Typically used with a Java 8 Lambda expression:
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter(Date.class, p -> p.after(new Date()))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the selector.
* Conversion to this type will be attempted, if necessary.
* @param genericSelector the {@link GenericSelector} to use.
@@ -854,21 +854,21 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B filter(Class<P> payloadType, GenericSelector<P> genericSelector) {
return filter(payloadType, genericSelector, null);
public <P> B filter(Class<P> expectedType, GenericSelector<P> genericSelector) {
return filter(expectedType, genericSelector, null);
}
/**
* Populate a {@link MessageFilter} with {@link MethodInvokingSelector}
* for the provided {@link GenericSelector}.
* In addition accept options for the integration endpoint using {@link FilterEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* In addition, accept options for the integration endpoint using {@link FilterEndpointSpec}.
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .filter(Date.class, p -> p.after(new Date()), e -> e.autoStartup(false))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the selector.
* Conversion to this type will be attempted, if necessary.
* @param genericSelector the {@link GenericSelector} to use.
@@ -878,13 +878,13 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @see LambdaMessageProcessor
* @see FilterEndpointSpec
*/
public <P> B filter(Class<P> payloadType, GenericSelector<P> genericSelector,
public <P> B filter(Class<P> expectedType, GenericSelector<P> genericSelector,
Consumer<FilterEndpointSpec> endpointConfigurer) {
Assert.notNull(genericSelector, "'genericSelector' must not be null");
MessageSelector selector = genericSelector instanceof MessageSelector ? (MessageSelector) genericSelector :
(ClassUtils.isLambda(genericSelector.getClass())
? new MethodInvokingSelector(new LambdaMessageProcessor(genericSelector, payloadType))
? new MethodInvokingSelector(new LambdaMessageProcessor(genericSelector, expectedType))
: new MethodInvokingSelector(genericSelector, ClassUtils.SELECTOR_ACCEPT_METHOD));
return this.register(new FilterEndpointSpec(new MessageFilter(selector)), endpointConfigurer);
}
@@ -1001,13 +1001,13 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* Populate a {@link ServiceActivatingHandler} for the
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
* to invoke the provided {@link GenericHandler} at runtime.
* Typically used with a Java 8 Lambda expression:
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .handle(Integer.class, (p, h) -> p / 2)
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the handler.
* Conversion to this type will be attempted, if necessary.
* @param handler the handler to invoke.
@@ -1015,8 +1015,8 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B handle(Class<P> payloadType, GenericHandler<P> handler) {
return handle(payloadType, handler, null);
public <P> B handle(Class<P> expectedType, GenericHandler<P> handler) {
return handle(expectedType, handler, null);
}
/**
@@ -1030,7 +1030,7 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* .handle(Integer.class, (p, h) -> p / 2, e -> e.autoStartup(false))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the handler.
* Conversion to this type will be attempted, if necessary.
* @param handler the handler to invoke.
@@ -1039,15 +1039,15 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B handle(Class<P> payloadType, GenericHandler<P> handler,
public <P> B handle(Class<P> expectedType, GenericHandler<P> handler,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
ServiceActivatingHandler serviceActivatingHandler;
if (ClassUtils.isLambda(handler.getClass())) {
serviceActivatingHandler = new ServiceActivatingHandler(new LambdaMessageProcessor(handler, payloadType));
serviceActivatingHandler = new ServiceActivatingHandler(new LambdaMessageProcessor(handler, expectedType));
}
else if (payloadType != null) {
return handle(payloadType, handler::handle, endpointConfigurer);
else if (expectedType != null) {
return handle(expectedType, handler::handle, endpointConfigurer);
}
else {
serviceActivatingHandler = new ServiceActivatingHandler(handler, ClassUtils.HANDLER_HANDLE_METHOD);
@@ -1488,7 +1488,7 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@link Function} at runtime.
* Typically used with a Java 8 Lambda expression:
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .split(String.class, p ->
@@ -1499,7 +1499,7 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* new Foo(rs.getInt(1), rs.getString(2)))))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the splitter.
* Conversion to this type will be attempted, if necessary.
* @param splitter the splitter {@link Function}.
@@ -1507,15 +1507,15 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P> B split(Class<P> payloadType, Function<P, ?> splitter) {
return split(payloadType, splitter, null);
public <P> B split(Class<P> expectedType, Function<P, ?> splitter) {
return split(expectedType, splitter, null);
}
/**
* Populate the {@link MethodInvokingSplitter} to evaluate the provided
* {@link Function} at runtime.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* In addition, accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .split(String.class, p ->
@@ -1527,7 +1527,7 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* , e -> e.applySequence(false))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the splitter.
* Conversion to this type will be attempted, if necessary.
* @param splitter the splitter {@link Function}.
@@ -1537,12 +1537,12 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @see LambdaMessageProcessor
* @see SplitterEndpointSpec
*/
public <P> B split(Class<P> payloadType, Function<P, ?> splitter,
public <P> B split(Class<P> expectedType, Function<P, ?> splitter,
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
MethodInvokingSplitter split =
ClassUtils.isLambda(splitter.getClass())
? new MethodInvokingSplitter(new LambdaMessageProcessor(splitter, payloadType))
? new MethodInvokingSplitter(new LambdaMessageProcessor(splitter, expectedType))
: new MethodInvokingSplitter(splitter, ClassUtils.FUNCTION_APPLY_METHOD);
return split(split, endpointConfigurer);
}
@@ -1880,8 +1880,8 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* .route(Integer.class, p -> p % 2 == 0)
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the splitter.
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the router.
* Conversion to this type will be attempted, if necessary.
* @param router the {@link Function} to use.
* @param <S> the source payload type or {@code Message.class}.
@@ -1889,15 +1889,15 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <S, T> B route(Class<S> payloadType, Function<S, T> router) {
return route(payloadType, router, null);
public <S, T> B route(Class<S> expectedType, Function<S, T> router) {
return route(expectedType, router, null);
}
/**
* Populate the {@link MethodInvokingRouter} for provided {@link Function}
* and payload type and options from {@link RouterSpec}.
* In addition accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically used with a Java 8 Lambda expression:
* In addition, accept options for the integration endpoint using {@link GenericEndpointSpec}.
* Typically, used with a Java 8 Lambda expression:
* <pre class="code">
* {@code
* .route(Integer.class, p -> p % 2 == 0,
@@ -1907,8 +1907,8 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* .applySequence(false))
* }
* </pre>
* @param payloadType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the splitter.
* @param expectedType the {@link Class} for expected payload type. It can also be
* {@code Message.class} if you wish to access the entire message in the router.
* Conversion to this type will be attempted, if necessary.
* @param router the {@link Function} to use.
* @param routerConfigurer the {@link Consumer} to provide {@link MethodInvokingRouter} options.
@@ -1917,12 +1917,12 @@ public abstract class BaseIntegrationFlowDefinition<B extends BaseIntegrationFlo
* @return the current {@link BaseIntegrationFlowDefinition}.
* @see LambdaMessageProcessor
*/
public <P, T> B route(Class<P> payloadType, Function<P, T> router,
public <P, T> B route(Class<P> expectedType, Function<P, T> router,
Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer) {
MethodInvokingRouter methodInvokingRouter =
ClassUtils.isLambda(router.getClass())
? new MethodInvokingRouter(new LambdaMessageProcessor(router, payloadType))
? new MethodInvokingRouter(new LambdaMessageProcessor(router, expectedType))
: new MethodInvokingRouter(router, ClassUtils.FUNCTION_APPLY_METHOD);
return route(new RouterSpec<>(methodInvokingRouter), routerConfigurer);
}

View File

@@ -30,6 +30,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.util.Assert;
@@ -53,13 +54,19 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
private final Method method;
private final Class<?> payloadType;
@Nullable
private final Class<?> expectedType;
private final Class<?>[] parameterTypes;
private MessageConverter messageConverter;
public LambdaMessageProcessor(Object target, Class<?> payloadType) {
/**
* Create a processor to evaluate a provided lambda at runtime against a request message.
* @param target the lambda object.
* @param expectedType an optional expected type for method argument conversion.
*/
public LambdaMessageProcessor(Object target, @Nullable Class<?> expectedType) {
Assert.notNull(target, "'target' must not be null");
this.target = target;
@@ -79,7 +86,7 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
this.method = methods.iterator().next();
this.method.setAccessible(true);
this.parameterTypes = this.method.getParameterTypes();
this.payloadType = payloadType;
this.expectedType = expectedType;
}
@Override
@@ -138,13 +145,13 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
}
}
else {
if (this.payloadType != null &&
!ClassUtils.isAssignable(this.payloadType, message.getPayload().getClass())) {
if (Message.class.isAssignableFrom(this.payloadType)) {
if (this.expectedType != null &&
!ClassUtils.isAssignable(this.expectedType, message.getPayload().getClass())) {
if (Message.class.isAssignableFrom(this.expectedType)) {
args[i] = message;
}
else {
args[i] = this.messageConverter.fromMessage(message, this.payloadType);
args[i] = this.messageConverter.fromMessage(message, this.expectedType);
}
}
else {

View File

@@ -580,9 +580,10 @@ Second, you can define the routing function within the DSL flow itself, as the f
@Bean
public IntegrationFlow routerFlow2() {
return IntegrationFlows.from("routingChannel")
.<Message<?>, String>route(m -> m.getHeaders().get("testHeader", String.class), m -> m
.channelMapping("someHeaderValue", "channelA")
.channelMapping("someOtherHeaderValue", "channelB"),
.route(Message.class, m -> m.getHeaders().get("testHeader", String.class),
m -> m
.channelMapping("someHeaderValue", "channelA")
.channelMapping("someOtherHeaderValue", "channelB"),
e -> e.id("headerValueRouter"))
.get();
}