Skip to content

Commit c38cccb

Browse files
committed
Make DefaultMethodInvokingMethodInterceptor modern Java compatible
* Also make `DefaultMethodInvokingMethodInterceptor` as `public`, so it can be used elsewhere, e.g., in the `GrpcInboundGateway` * Fix nullability in the `MessageToBinaryConverter`
1 parent f0d8135 commit c38cccb

File tree

2 files changed

+29
-149
lines changed

2 files changed

+29
-149
lines changed

spring-integration-core/src/main/java/org/springframework/integration/gateway/DefaultMethodInvokingMethodInterceptor.java

Lines changed: 23 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@
2020
import java.lang.invoke.MethodHandles;
2121
import java.lang.invoke.MethodHandles.Lookup;
2222
import java.lang.invoke.MethodType;
23-
import java.lang.reflect.Constructor;
2423
import java.lang.reflect.Method;
24+
import java.lang.reflect.Modifier;
2525
import java.util.Map;
26-
import java.util.function.Supplier;
2726

2827
import org.aopalliance.intercept.MethodInterceptor;
2928
import org.aopalliance.intercept.MethodInvocation;
@@ -32,10 +31,9 @@
3231
import org.springframework.aop.ProxyMethodInvocation;
3332
import org.springframework.util.ConcurrentReferenceHashMap;
3433
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
35-
import org.springframework.util.ReflectionUtils;
3634

3735
/**
38-
* Method interceptor to invoke default methods on the gateway proxy.
36+
* Method interceptor to invoke default methods from the interfaces on the proxy.
3937
* <p>
4038
* The copy of {@code DefaultMethodInvokingMethodInterceptor} from Spring Data Commons.
4139
*
@@ -46,167 +44,48 @@
4644
*
4745
* @since 5.3
4846
*/
49-
class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
47+
public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
5048

51-
private final MethodHandleLookup methodHandleLookup = MethodHandleLookup.getMethodHandleLookup();
49+
private static final Lookup LOOKUP = MethodHandles.lookup();
5250

5351
private final Map<Method, MethodHandle> methodHandleCache =
5452
new ConcurrentReferenceHashMap<>(10, ReferenceType.WEAK);
5553

5654
@Override
57-
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { // NOSONAR
55+
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {
5856
Method method = invocation.getMethod();
5957
if (!method.isDefault()) {
6058
return invocation.proceed();
6159
}
62-
@Nullable Object[] arguments = invocation.getArguments();
6360
Object proxy = ((ProxyMethodInvocation) invocation).getProxy();
61+
@Nullable Object[] arguments = invocation.getArguments();
6462
return getMethodHandle(method)
6563
.bindTo(proxy)
6664
.invokeWithArguments(arguments);
6765
}
6866

69-
private MethodHandle getMethodHandle(Method method) {
70-
return this.methodHandleCache.computeIfAbsent(method,
71-
(key) -> {
72-
try {
73-
return this.methodHandleLookup.lookup(key);
74-
}
75-
catch (ReflectiveOperationException ex) {
76-
throw new IllegalStateException(ex);
77-
}
78-
});
67+
private MethodHandle getMethodHandle(Method method) throws Exception {
68+
return this.methodHandleCache.computeIfAbsent(method, DefaultMethodInvokingMethodInterceptor::lookup);
7969
}
8070

81-
enum MethodHandleLookup {
82-
83-
/**
84-
* Encapsulated {@link MethodHandle} lookup working on Java 9.
85-
*/
86-
ENCAPSULATED {
87-
88-
@Nullable
89-
private final transient Method privateLookupIn =
90-
ReflectionUtils.findMethod(MethodHandles.class, "privateLookupIn", Class.class, Lookup.class);
91-
92-
@Override
93-
MethodHandle lookup(Method method) throws ReflectiveOperationException {
94-
if (this.privateLookupIn == null) {
95-
throw new IllegalStateException("Could not obtain MethodHandles.privateLookupIn!");
96-
}
97-
return doLookup(method, getLookup(method.getDeclaringClass(), this.privateLookupIn));
98-
}
99-
100-
@Override
101-
boolean isAvailable() {
102-
return this.privateLookupIn != null;
103-
}
104-
105-
private Lookup getLookup(Class<?> declaringClass, Method privateLookupIn) {
106-
Lookup lookup = MethodHandles.lookup();
107-
try {
108-
return (Lookup) privateLookupIn.invoke(MethodHandles.class, declaringClass, lookup);
109-
}
110-
catch (ReflectiveOperationException e) {
111-
return lookup;
112-
}
113-
}
114-
115-
},
116-
117-
/**
118-
* Open (via reflection construction of {@link Lookup}) method handle lookup. Works with Java 8 and
119-
* with Java 9 permitting illegal access.
120-
*/
121-
OPEN {
122-
123-
private volatile boolean constructorResolved;
124-
125-
private transient @Nullable Constructor<Lookup> constructor;
126-
127-
private final Supplier<@Nullable Constructor<Lookup>> constructorSupplier =
128-
() -> {
129-
if (!this.constructorResolved) {
130-
Constructor<Lookup> ctor = null;
131-
try {
132-
ctor = Lookup.class.getDeclaredConstructor(Class.class);
133-
ReflectionUtils.makeAccessible(ctor);
134-
}
135-
catch (Exception ex) {
136-
// this is the signal that we are on Java 9 (encapsulated) and can't use the accessible
137-
// constructor approach.
138-
if (!ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
139-
throw new IllegalStateException(ex);
140-
}
141-
}
142-
this.constructor = ctor;
143-
this.constructorResolved = true;
144-
}
145-
return this.constructor;
146-
};
147-
148-
@Override
149-
MethodHandle lookup(Method method) throws ReflectiveOperationException {
150-
Constructor<Lookup> lookupConstructor = this.constructorSupplier.get();
151-
if (lookupConstructor != null) {
152-
return lookupConstructor.newInstance(method.getDeclaringClass())
153-
.unreflectSpecial(method, method.getDeclaringClass());
154-
}
155-
else {
156-
throw new IllegalStateException("Could not obtain MethodHandles.lookup constructor!");
157-
}
158-
}
159-
160-
@Override
161-
boolean isAvailable() {
162-
return this.constructorSupplier.get() != null;
163-
}
164-
165-
},
166-
167-
/**
168-
* Fallback {@link MethodHandle} lookup using {@link MethodHandles#lookup() public lookup}.
169-
*/
170-
FALLBACK {
171-
@Override
172-
MethodHandle lookup(Method method) throws ReflectiveOperationException {
173-
return doLookup(method, MethodHandles.lookup());
174-
}
175-
176-
@Override
177-
boolean isAvailable() {
178-
return true;
179-
}
180-
181-
};
182-
183-
private static MethodHandle doLookup(Method method, Lookup lookup) throws ReflectiveOperationException {
71+
/**
72+
* Lookup a {@link MethodHandle} given {@link Method} to look up.
73+
* @param method must not be {@literal null}.
74+
* @return the method handle.
75+
*/
76+
private static MethodHandle lookup(Method method) {
77+
try {
78+
Class<?> declaringClass = method.getDeclaringClass();
79+
Lookup lookup = MethodHandles.privateLookupIn(declaringClass, LOOKUP);
18480
MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
185-
return lookup.findSpecial(method.getDeclaringClass(), method.getName(),
186-
methodType, method.getDeclaringClass());
187-
}
188-
189-
abstract MethodHandle lookup(Method method) throws ReflectiveOperationException;
19081

191-
/**
192-
* @return {@literal true} if the lookup is available.
193-
*/
194-
abstract boolean isAvailable();
195-
196-
/**
197-
* Obtain the first available {@link MethodHandleLookup}.
198-
* @return the {@link MethodHandleLookup}
199-
* @throws IllegalStateException if no {@link MethodHandleLookup} is available.
200-
*/
201-
static MethodHandleLookup getMethodHandleLookup() {
202-
for (MethodHandleLookup it : MethodHandleLookup.values()) {
203-
if (it.isAvailable()) {
204-
return it;
205-
}
206-
}
207-
throw new IllegalStateException("No MethodHandleLookup available!");
82+
return Modifier.isStatic(method.getModifiers())
83+
? lookup.findStatic(declaringClass, method.getName(), methodType)
84+
: lookup.findSpecial(declaringClass, method.getName(), methodType, declaringClass);
85+
}
86+
catch (Exception ex) {
87+
throw new IllegalStateException(ex);
20888
}
209-
21089
}
21190

21291
}

spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/support/MessageToBinaryConverter.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616

1717
package org.springframework.integration.mongodb.support;
1818

19-
import java.util.Objects;
20-
2119
import org.bson.types.Binary;
20+
import org.jspecify.annotations.Nullable;
2221

2322
import org.springframework.core.convert.converter.Converter;
2423
import org.springframework.core.serializer.support.SerializingConverter;
@@ -27,16 +26,18 @@
2726

2827
/**
2928
* @author Artem Bilan
29+
*
3030
* @since 5.0
3131
*/
3232
@WritingConverter
33-
public class MessageToBinaryConverter implements Converter<Message<?>, Binary> {
33+
public class MessageToBinaryConverter implements Converter<Message<?>, @Nullable Binary> {
3434

3535
private final Converter<Object, byte[]> serializingConverter = new SerializingConverter();
3636

3737
@Override
38-
public Binary convert(Message<?> source) {
39-
return new Binary(Objects.requireNonNull(this.serializingConverter.convert(source)));
38+
public @Nullable Binary convert(Message<?> source) {
39+
byte[] data = this.serializingConverter.convert(source);
40+
return data != null ? new Binary(data) : null;
4041
}
4142

4243
}

0 commit comments

Comments
 (0)