Expose more reflection hints (#3951)

* Expose more reflection hints

* Bring back `@Reflective` on `Pausable` - for possible end-user usage
* Add `ReactiveMessageHandler` hint since its method is used reflectively
in the `IntegrationRSocketMessageHandler`
* Add `@Reflective` on the `ServerRSocketMessageHandler.handleConnectionSetup()`
since it is used reflectively for a `registerHandlerMethod()`
* Add `KafkaRuntimeHints` to expose `Pausable` contract on Kafka inbound endpoints
for SpEL invocation via Control Bus
* Document native images support

* Fix language in docs

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

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2022-11-18 11:26:46 -05:00
committed by GitHub
parent 18a87eaf2a
commit 3c40b01915
10 changed files with 136 additions and 2 deletions

View File

@@ -63,6 +63,7 @@ import org.springframework.integration.support.MutableMessageHeaders;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.ReactiveMessageHandler;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.util.ReflectionUtils;
@@ -83,6 +84,7 @@ class CoreRuntimeHints implements RuntimeHintsRegistrar {
GenericSelector.class,
GenericTransformer.class,
GenericHandler.class,
ReactiveMessageHandler.class,
Function.class,
Supplier.class,
BeanExpressionContext.class,

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.core;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.integration.support.management.ManageableLifecycle;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
@@ -37,12 +38,14 @@ public interface Pausable extends ManageableLifecycle {
* Pause the endpoint.
*/
@ManagedOperation(description = "Pause the component")
@Reflective
void pause();
/**
* Resume the endpoint if paused.
*/
@ManagedOperation(description = "Resume the component")
@Reflective
void resume();
/**
@@ -51,6 +54,7 @@ public interface Pausable extends ManageableLifecycle {
* @since 5.4
*/
@ManagedAttribute(description = "Is the component paused?")
@Reflective
default boolean isPaused() {
throw new UnsupportedOperationException("This component does not implement this method");
}

View File

@@ -25,7 +25,7 @@ import org.springframework.web.HttpRequestHandler;
import org.springframework.web.server.WebHandler;
/**
* {@link RuntimeHintsRegistrar} for Spring Integration core module.
* {@link RuntimeHintsRegistrar} for Spring Integration HTTP module.
*
* @author Artem Bilan
*

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2022 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.kafka.aot;
import java.util.stream.Stream;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.integration.kafka.inbound.KafkaInboundGateway;
import org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter;
import org.springframework.integration.kafka.inbound.KafkaMessageSource;
import org.springframework.util.ReflectionUtils;
/**
* {@link RuntimeHintsRegistrar} for Spring Integration Kafka module.
*
* @author Artem Bilan
*
* @since 6.0
*/
class KafkaRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ReflectionHints reflectionHints = hints.reflection();
// Java DSL does not register beans during AOT phase, so @Reflective is not reachable from Pausable
Stream.of("pause", "resume", "isPaused")
.flatMap((name) ->
Stream.of(KafkaMessageDrivenChannelAdapter.class,
KafkaInboundGateway.class,
KafkaMessageSource.class)
.filter((type) -> reflectionHints.getTypeHint(type) == null)
.flatMap((type) -> Stream.ofNullable(ReflectionUtils.findMethod(type, name))))
.forEach(method -> reflectionHints.registerMethod(method, ExecutableMode.INVOKE));
}
}

View File

@@ -0,0 +1 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.integration.kafka.aot.KafkaRuntimeHints

View File

@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.io.buffer.DataBuffer;
@@ -138,6 +139,7 @@ public class ServerRSocketMessageHandler extends IntegrationRSocketMessageHandle
}
@SuppressWarnings("unused")
@Reflective
private void handleConnectionSetup(Message<DataBuffer> connectMessage) {
DataBuffer dataBuffer = connectMessage.getPayload();
MessageHeaders messageHeaders = connectMessage.getHeaders();

View File

@@ -41,6 +41,8 @@ include::./system-management.adoc[]
include::./reactive-streams.adoc[]
include::./native-aot.adoc[]
include::./endpoint-summary.adoc[]
include::./amqp.adoc[]

View File

@@ -28,6 +28,7 @@ Welcome to the Spring Integration reference documentation!
<<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>> :: Details about Kotlin DSL for EIP
<<./system-management.adoc#system-management-chapter,System Management>> :: Message store, control bus, integration graph, metrics, JMX
<<./reactive-streams.adoc#reactive-streams,Reactive Streams Support>> :: Details about Reactive Streams support: message channels, channel adapters etc.
<<./native-aot.adoc#native-images-support,Native Images Support>> :: GraalVM native images and Spring AOT support
[horizontal]
**Integration Endpoints** ::

View File

@@ -0,0 +1,57 @@
[[native-images-support]]
=== Native Images Support
Starting with version 6.0, GraalVM compilation of Spring Integration applications to native images is supported by https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aot[Spring AOT] native hints.
For most common use cases, such as endpoint definitions with `@Bean` methods, Java DSL configuration with lambdas and `@MessagingGateway` interface scanning (importing), the framework provides respective reflection, proxy and serialization hints.
If configuration uses messaging annotations (`@ServiceActivator`, `@Splitter` etc.) on POJO methods, or POJO methods are used with the `IntegrationFlowBuilder.handle(Object service, String methodName)` API, they have to be also marked with a `@Reflective` annotation since they are invoked by the framework reflectively.
IMPORTANT: XML configuration is not supported for native images.
As stated before, service interfaces with the `@MessagingGateway` annotation, when they are scanned by the `@IntegrationComponentScan` or used in an `@Import` annotation, are processed by the framework and the respective proxy hint is exposed into the AOT contribution.
When gateways are declared using the `IntegrationFlow.from(Class<?> serviceInterface)` API, the proxy configured for such interfaces have to be exposed manually:
====
[source,java]
----
@Configuration
@EnableIntegration
@ImportRuntimeHints(GatewayRuntimeHints.class)
public class IntegrationConfiguration {
@Bean
IntegrationFlow someFlow() {
return IntegrationFlow.from(SomeGateway)
// ...
.get();
}
public interface SomeGateway {
void doSomething(Object payload);
}
private static class GatewayRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.proxies().registerJdkProxy(
AopProxyUtils.completeJdkProxyInterfaces(SomeGateway));
}
}
}
----
====
NOTE: The `IntegrationFlow` content is not processed during the AOT processing phase.
Therefore, some hints, such as the one mentioned above for a gateway proxy, must be provided by the target application.
Of course, configuration is just a piece of an integration solution.
The most important part is data transferring over the network as well as persistent storage.
That's where serialization comes handy for many use-cases.
Spring Integration exposes serialization hints into a native image configuration for these types used by the framework internally: `String`, `Number`, `Long`, `Date`, `ArrayList`, `HashMap`, `Properties`, `Hashtable`, `Exception`, `UUID`, `GenericMessage`, `ErrorMessage`, `MessageHeaders`, `AdviceMessage`, `MutableMessage`, `MutableMessageHeaders`, `MessageGroupMetadata`, `MessageHolder`, `MessageMetadata`, `MessageHistory`, `MessageHistory.Entry`, `DelayHandler.DelayedMessageWrapper`.
For user specific data, mostly present as a message payload, the serialization hint must be exposed manually via a `RuntimeHintsRegistrar` implementation, as is shown above for a gateway proxy, and the respective `RuntimeHints.serialization().registerType()` API.
NOTE: It is recommended that native integration applications are developed with Spring Boot, using its respective build tools.

View File

@@ -52,7 +52,7 @@ The Java DSL (see `org.springframework.integration.smb.dsl.Smb` factory) also ha
An `SmbStreamingMessageSource` and `SmbOutboundGateway` implementation are introduced.
See <<./smb.adoc#smb,SMB Support>> for more information.
[[x6.0-jdbc]]
[[x6.0-postgres]]
==== PostgreSQL Push Notification
A `PostgresSubscribableChannel` allows to receive push notifications via `PostgresChannelMessageTableSubscriber` upon new messages add to the `JdbcChannelMessageStore`.
@@ -98,6 +98,12 @@ Kotlin Coroutines support has been introduced to the framework.
See <<./kotlin-functions.adoc#kotlin-coroutines,Kotlin Coroutines>> for more information.
[[x6.0-aot]]
==== Native Images
Support for creating GraalVM native images is provided.
See <<./native-aot.adoc#native-images-support,Native Images Support>> for more information.
[[x6.0-general]]
=== General Changes
@@ -160,6 +166,7 @@ The `spring-integration-gemfire` module has been removed altogether since there
The `#cookies` variable for expression evaluation context, exposed in the `HttpRequestHandlingEndpointSupport`, is now a `MultiValueMap` to carry all the values for cookies set by the client.
See <<./http.adoc#http,HTTP Support>> for more information.
[[x6.0-kafka]]
=== Apache Kafka Changes
When providing a `RetryTemplate` on the inbound gateway or message-driven channel adapter, if an `errorChannel` is also provided, an `ErrorMessageSendingRecoverer` is automatically configured.
@@ -168,24 +175,28 @@ In addition, the new `KafkaErrorMessageSendingRecoverer` is provided; this can b
See <<./kafka.adoc#kafka,Spring for Apache Kafka Support>> for more information.
[[x6.0-jdbc]]
=== JDBC Changes
The `DefaultLockRepository` can now be supplied with a `PlatformTransactionManager` instead of relying on the primary bean from the application context.
See <<./jdbc.adoc#jdbc-lock-registry,JDBC Lock Registry>> for more information.
[[x6.0-tcp]]
=== TCP/IP Changes
The `lookupHost` property of the `AbstractConnectionFactory` and `DatagramPacketMessageMapper` is now set to `false` by default to avoid delays in the environments where DNS is not configured.
See <<./ip.adoc#ip,TCP and UDP Support>> for more information.
[[x6.0-jms]]
=== JMS Changes
The `JmsOutboundGateway` now creates a `TemporaryTopic` instead of `TemporaryQueue` if `replyPubSubDomain` option is set to `true`.
See <<./jms.adoc#jms,JMS Support>> for more information.
[[x6.0-security]]
=== Security Changes
The `ChannelSecurityInterceptor` and its annotation `@SecuredChannel` and XML `<secured-channels>` configurations have been deprecated in favor of `AuthorizationChannelInterceptor`.