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:
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.integration.kafka.aot.KafkaRuntimeHints
|
||||
@@ -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();
|
||||
|
||||
@@ -41,6 +41,8 @@ include::./system-management.adoc[]
|
||||
|
||||
include::./reactive-streams.adoc[]
|
||||
|
||||
include::./native-aot.adoc[]
|
||||
|
||||
include::./endpoint-summary.adoc[]
|
||||
|
||||
include::./amqp.adoc[]
|
||||
|
||||
@@ -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** ::
|
||||
|
||||
57
src/reference/asciidoc/native-aot.adoc
Normal file
57
src/reference/asciidoc/native-aot.adoc
Normal 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.
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user