diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aot/CoreRuntimeHints.java b/spring-integration-core/src/main/java/org/springframework/integration/aot/CoreRuntimeHints.java index 826d28809b7..2da6b185103 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/aot/CoreRuntimeHints.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aot/CoreRuntimeHints.java @@ -63,6 +63,7 @@ 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 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) { GenericSelector.class, GenericTransformer.class, GenericHandler.class, + ReactiveMessageHandler.class, Function.class, Supplier.class, BeanExpressionContext.class, diff --git a/spring-integration-core/src/main/java/org/springframework/integration/core/Pausable.java b/spring-integration-core/src/main/java/org/springframework/integration/core/Pausable.java index 4fa2c895790..68d6b685c78 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/core/Pausable.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/core/Pausable.java @@ -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"); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/aot/HttpRuntimeHints.java b/spring-integration-http/src/main/java/org/springframework/integration/http/aot/HttpRuntimeHints.java index fb229308f81..759d19908a3 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/aot/HttpRuntimeHints.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/aot/HttpRuntimeHints.java @@ -25,7 +25,7 @@ import org.springframework.web.server.WebHandler; /** - * {@link RuntimeHintsRegistrar} for Spring Integration core module. + * {@link RuntimeHintsRegistrar} for Spring Integration HTTP module. * * @author Artem Bilan * diff --git a/spring-integration-kafka/src/main/java/org/springframework/integration/kafka/aot/KafkaRuntimeHints.java b/spring-integration-kafka/src/main/java/org/springframework/integration/kafka/aot/KafkaRuntimeHints.java new file mode 100644 index 00000000000..fd008824cc6 --- /dev/null +++ b/spring-integration-kafka/src/main/java/org/springframework/integration/kafka/aot/KafkaRuntimeHints.java @@ -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)); + } + +} diff --git a/spring-integration-kafka/src/main/resources/META-INF/spring/aot.factories b/spring-integration-kafka/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 00000000000..01a385987a5 --- /dev/null +++ b/spring-integration-kafka/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.integration.kafka.aot.KafkaRuntimeHints diff --git a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/ServerRSocketMessageHandler.java b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/ServerRSocketMessageHandler.java index 8106b442bd5..0690bbbf6cd 100644 --- a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/ServerRSocketMessageHandler.java +++ b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/ServerRSocketMessageHandler.java @@ -23,6 +23,7 @@ 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 @@ void registerHandleConnectionSetupMethod() { } @SuppressWarnings("unused") + @Reflective private void handleConnectionSetup(Message connectMessage) { DataBuffer dataBuffer = connectMessage.getPayload(); MessageHeaders messageHeaders = connectMessage.getHeaders(); diff --git a/src/reference/asciidoc/index-single.adoc b/src/reference/asciidoc/index-single.adoc index aae8b157cf3..319ea5f233a 100644 --- a/src/reference/asciidoc/index-single.adoc +++ b/src/reference/asciidoc/index-single.adoc @@ -41,6 +41,8 @@ include::./system-management.adoc[] include::./reactive-streams.adoc[] +include::./native-aot.adoc[] + include::./endpoint-summary.adoc[] include::./amqp.adoc[] diff --git a/src/reference/asciidoc/index.adoc b/src/reference/asciidoc/index.adoc index af8a0e295dc..2bb299ea59f 100644 --- a/src/reference/asciidoc/index.adoc +++ b/src/reference/asciidoc/index.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** :: diff --git a/src/reference/asciidoc/native-aot.adoc b/src/reference/asciidoc/native-aot.adoc new file mode 100644 index 00000000000..850c7ab6c90 --- /dev/null +++ b/src/reference/asciidoc/native-aot.adoc @@ -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. \ No newline at end of file diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 18893ecdc77..e69e06118bc 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -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 `` configurations have been deprecated in favor of `AuthorizationChannelInterceptor`.