Skip to content

Commit 3c40b01

Browse files
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 <[email protected]> Co-authored-by: Gary Russell <[email protected]>
1 parent 18a87ea commit 3c40b01

File tree

10 files changed

+136
-2
lines changed

10 files changed

+136
-2
lines changed

spring-integration-core/src/main/java/org/springframework/integration/aot/CoreRuntimeHints.java

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.springframework.messaging.MessageHandler;
6464
import org.springframework.messaging.MessageHeaders;
6565
import org.springframework.messaging.PollableChannel;
66+
import org.springframework.messaging.ReactiveMessageHandler;
6667
import org.springframework.messaging.support.ErrorMessage;
6768
import org.springframework.messaging.support.GenericMessage;
6869
import org.springframework.util.ReflectionUtils;
@@ -83,6 +84,7 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
8384
GenericSelector.class,
8485
GenericTransformer.class,
8586
GenericHandler.class,
87+
ReactiveMessageHandler.class,
8688
Function.class,
8789
Supplier.class,
8890
BeanExpressionContext.class,

spring-integration-core/src/main/java/org/springframework/integration/core/Pausable.java

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.core;
1818

19+
import org.springframework.aot.hint.annotation.Reflective;
1920
import org.springframework.integration.support.management.ManageableLifecycle;
2021
import org.springframework.jmx.export.annotation.ManagedAttribute;
2122
import org.springframework.jmx.export.annotation.ManagedOperation;
@@ -37,12 +38,14 @@ public interface Pausable extends ManageableLifecycle {
3738
* Pause the endpoint.
3839
*/
3940
@ManagedOperation(description = "Pause the component")
41+
@Reflective
4042
void pause();
4143

4244
/**
4345
* Resume the endpoint if paused.
4446
*/
4547
@ManagedOperation(description = "Resume the component")
48+
@Reflective
4649
void resume();
4750

4851
/**
@@ -51,6 +54,7 @@ public interface Pausable extends ManageableLifecycle {
5154
* @since 5.4
5255
*/
5356
@ManagedAttribute(description = "Is the component paused?")
57+
@Reflective
5458
default boolean isPaused() {
5559
throw new UnsupportedOperationException("This component does not implement this method");
5660
}

spring-integration-http/src/main/java/org/springframework/integration/http/aot/HttpRuntimeHints.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.springframework.web.server.WebHandler;
2626

2727
/**
28-
* {@link RuntimeHintsRegistrar} for Spring Integration core module.
28+
* {@link RuntimeHintsRegistrar} for Spring Integration HTTP module.
2929
*
3030
* @author Artem Bilan
3131
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.kafka.aot;
18+
19+
import java.util.stream.Stream;
20+
21+
import org.springframework.aot.hint.ExecutableMode;
22+
import org.springframework.aot.hint.ReflectionHints;
23+
import org.springframework.aot.hint.RuntimeHints;
24+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
25+
import org.springframework.integration.kafka.inbound.KafkaInboundGateway;
26+
import org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter;
27+
import org.springframework.integration.kafka.inbound.KafkaMessageSource;
28+
import org.springframework.util.ReflectionUtils;
29+
30+
/**
31+
* {@link RuntimeHintsRegistrar} for Spring Integration Kafka module.
32+
*
33+
* @author Artem Bilan
34+
*
35+
* @since 6.0
36+
*/
37+
class KafkaRuntimeHints implements RuntimeHintsRegistrar {
38+
39+
@Override
40+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
41+
ReflectionHints reflectionHints = hints.reflection();
42+
43+
// Java DSL does not register beans during AOT phase, so @Reflective is not reachable from Pausable
44+
Stream.of("pause", "resume", "isPaused")
45+
.flatMap((name) ->
46+
Stream.of(KafkaMessageDrivenChannelAdapter.class,
47+
KafkaInboundGateway.class,
48+
KafkaMessageSource.class)
49+
.filter((type) -> reflectionHints.getTypeHint(type) == null)
50+
.flatMap((type) -> Stream.ofNullable(ReflectionUtils.findMethod(type, name))))
51+
.forEach(method -> reflectionHints.registerMethod(method, ExecutableMode.INVOKE));
52+
}
53+
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.integration.kafka.aot.KafkaRuntimeHints

spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/ServerRSocketMessageHandler.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424
import java.util.function.BiFunction;
2525

26+
import org.springframework.aot.hint.annotation.Reflective;
2627
import org.springframework.context.ApplicationEventPublisher;
2728
import org.springframework.context.ApplicationEventPublisherAware;
2829
import org.springframework.core.io.buffer.DataBuffer;
@@ -138,6 +139,7 @@ void registerHandleConnectionSetupMethod() {
138139
}
139140

140141
@SuppressWarnings("unused")
142+
@Reflective
141143
private void handleConnectionSetup(Message<DataBuffer> connectMessage) {
142144
DataBuffer dataBuffer = connectMessage.getPayload();
143145
MessageHeaders messageHeaders = connectMessage.getHeaders();

src/reference/asciidoc/index-single.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ include::./system-management.adoc[]
4141

4242
include::./reactive-streams.adoc[]
4343

44+
include::./native-aot.adoc[]
45+
4446
include::./endpoint-summary.adoc[]
4547

4648
include::./amqp.adoc[]

src/reference/asciidoc/index.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Welcome to the Spring Integration reference documentation!
2828
<<./kotlin-dsl.adoc#kotlin-dsl,Kotlin DSL>> :: Details about Kotlin DSL for EIP
2929
<<./system-management.adoc#system-management-chapter,System Management>> :: Message store, control bus, integration graph, metrics, JMX
3030
<<./reactive-streams.adoc#reactive-streams,Reactive Streams Support>> :: Details about Reactive Streams support: message channels, channel adapters etc.
31+
<<./native-aot.adoc#native-images-support,Native Images Support>> :: GraalVM native images and Spring AOT support
3132

3233
[horizontal]
3334
**Integration Endpoints** ::
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[[native-images-support]]
2+
=== Native Images Support
3+
4+
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.
5+
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.
6+
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.
7+
8+
IMPORTANT: XML configuration is not supported for native images.
9+
10+
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.
11+
When gateways are declared using the `IntegrationFlow.from(Class<?> serviceInterface)` API, the proxy configured for such interfaces have to be exposed manually:
12+
13+
====
14+
[source,java]
15+
----
16+
@Configuration
17+
@EnableIntegration
18+
@ImportRuntimeHints(GatewayRuntimeHints.class)
19+
public class IntegrationConfiguration {
20+
21+
@Bean
22+
IntegrationFlow someFlow() {
23+
return IntegrationFlow.from(SomeGateway)
24+
// ...
25+
.get();
26+
}
27+
28+
public interface SomeGateway {
29+
30+
void doSomething(Object payload);
31+
32+
}
33+
34+
private static class GatewayRuntimeHints implements RuntimeHintsRegistrar {
35+
36+
@Override
37+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
38+
hints.proxies().registerJdkProxy(
39+
AopProxyUtils.completeJdkProxyInterfaces(SomeGateway));
40+
}
41+
42+
}
43+
44+
}
45+
----
46+
====
47+
48+
NOTE: The `IntegrationFlow` content is not processed during the AOT processing phase.
49+
Therefore, some hints, such as the one mentioned above for a gateway proxy, must be provided by the target application.
50+
51+
Of course, configuration is just a piece of an integration solution.
52+
The most important part is data transferring over the network as well as persistent storage.
53+
That's where serialization comes handy for many use-cases.
54+
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`.
55+
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.
56+
57+
NOTE: It is recommended that native integration applications are developed with Spring Boot, using its respective build tools.

src/reference/asciidoc/whats-new.adoc

+12-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The Java DSL (see `org.springframework.integration.smb.dsl.Smb` factory) also ha
5252
An `SmbStreamingMessageSource` and `SmbOutboundGateway` implementation are introduced.
5353
See <<./smb.adoc#smb,SMB Support>> for more information.
5454

55-
[[x6.0-jdbc]]
55+
[[x6.0-postgres]]
5656
==== PostgreSQL Push Notification
5757

5858
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.
9898

9999
See <<./kotlin-functions.adoc#kotlin-coroutines,Kotlin Coroutines>> for more information.
100100

101+
[[x6.0-aot]]
102+
==== Native Images
103+
104+
Support for creating GraalVM native images is provided.
105+
See <<./native-aot.adoc#native-images-support,Native Images Support>> for more information.
106+
101107
[[x6.0-general]]
102108
=== General Changes
103109

@@ -160,6 +166,7 @@ The `spring-integration-gemfire` module has been removed altogether since there
160166
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.
161167
See <<./http.adoc#http,HTTP Support>> for more information.
162168

169+
[[x6.0-kafka]]
163170
=== Apache Kafka Changes
164171

165172
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
168175

169176
See <<./kafka.adoc#kafka,Spring for Apache Kafka Support>> for more information.
170177

178+
[[x6.0-jdbc]]
171179
=== JDBC Changes
172180

173181
The `DefaultLockRepository` can now be supplied with a `PlatformTransactionManager` instead of relying on the primary bean from the application context.
174182

175183
See <<./jdbc.adoc#jdbc-lock-registry,JDBC Lock Registry>> for more information.
176184

185+
[[x6.0-tcp]]
177186
=== TCP/IP Changes
178187

179188
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.
180189

181190
See <<./ip.adoc#ip,TCP and UDP Support>> for more information.
182191

192+
[[x6.0-jms]]
183193
=== JMS Changes
184194

185195
The `JmsOutboundGateway` now creates a `TemporaryTopic` instead of `TemporaryQueue` if `replyPubSubDomain` option is set to `true`.
186196

187197
See <<./jms.adoc#jms,JMS Support>> for more information.
188198

199+
[[x6.0-security]]
189200
=== Security Changes
190201

191202
The `ChannelSecurityInterceptor` and its annotation `@SecuredChannel` and XML `<secured-channels>` configurations have been deprecated in favor of `AuthorizationChannelInterceptor`.

0 commit comments

Comments
 (0)