GH-3765 Resolve Lookup ctor lazily via reflection

Fixes https://github.com/spring-projects/spring-integration/issues/3765

The `DefaultMethodInvokingMethodInterceptor` has several strategies
for `default` method invocations in the messaging gateway.
One of them is based on the `Lookup` constructor which causes
a `WARNING: An illegal reflective access operation has occurred`.
This can be done lazily when there is no `MethodHandles.privateLookupIn`
in Java version used for the project.

* Fix `OPEN` enum value for the `Constructor<Lookup>` to be resolved in
a lazy manner via `Supplier` and `boolean` flag

**Cherry-pick to `5.5.x`**
This commit is contained in:
Artem Bilan
2022-04-11 12:24:00 -04:00
committed by Gary Russell
parent 6a435fbd77
commit cd84f1699a

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2021 the original author or authors. * Copyright 2015-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
@@ -119,29 +120,36 @@ class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
*/ */
OPEN { OPEN {
@Nullable private volatile boolean constructorResolved;
private final transient Constructor<Lookup> constructor;
{ private Constructor<Lookup> constructor;
Constructor<Lookup> ctor = null;
try { private final Supplier<Constructor<Lookup>> constructorSupplier =
ctor = Lookup.class.getDeclaredConstructor(Class.class); () -> {
ReflectionUtils.makeAccessible(ctor); if (!this.constructorResolved) {
} Constructor<Lookup> ctor = null;
catch (Exception ex) { try {
// this is the signal that we are on Java 9 (encapsulated) and can't use the accessible constructor ctor = Lookup.class.getDeclaredConstructor(Class.class);
// approach. ReflectionUtils.makeAccessible(ctor);
if (!ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { }
throw new IllegalStateException(ex); catch (Exception ex) {
} // this is the signal that we are on Java 9 (encapsulated) and can't use the accessible
} // constructor approach.
this.constructor = ctor; if (!ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
} throw new IllegalStateException(ex);
}
}
this.constructor = ctor;
this.constructorResolved = true;
}
return this.constructor;
};
@Override @Override
MethodHandle lookup(Method method) throws ReflectiveOperationException { MethodHandle lookup(Method method) throws ReflectiveOperationException {
if (this.constructor != null) { Constructor<Lookup> lookupConstructor = this.constructorSupplier.get();
return this.constructor.newInstance(method.getDeclaringClass()) if (lookupConstructor != null) {
return lookupConstructor.newInstance(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass()); .unreflectSpecial(method, method.getDeclaringClass());
} }
else { else {
@@ -151,7 +159,7 @@ class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
@Override @Override
boolean isAvailable() { boolean isAvailable() {
return this.constructor != null; return this.constructorSupplier.get() != null;
} }
}, },
@@ -160,7 +168,6 @@ class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
* Fallback {@link MethodHandle} lookup using {@link MethodHandles#lookup() public lookup}. * Fallback {@link MethodHandle} lookup using {@link MethodHandles#lookup() public lookup}.
*/ */
FALLBACK { FALLBACK {
@Override @Override
MethodHandle lookup(Method method) throws ReflectiveOperationException { MethodHandle lookup(Method method) throws ReflectiveOperationException {
return doLookup(method, MethodHandles.lookup()); return doLookup(method, MethodHandles.lookup());
@@ -191,7 +198,7 @@ class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
* @return the {@link MethodHandleLookup} * @return the {@link MethodHandleLookup}
* @throws IllegalStateException if no {@link MethodHandleLookup} is available. * @throws IllegalStateException if no {@link MethodHandleLookup} is available.
*/ */
public static MethodHandleLookup getMethodHandleLookup() { static MethodHandleLookup getMethodHandleLookup() {
for (MethodHandleLookup it : MethodHandleLookup.values()) { for (MethodHandleLookup it : MethodHandleLookup.values()) {
if (it.isAvailable()) { if (it.isAvailable()) {
return it; return it;