
io.lettuce.core.internal.DefaultMethods Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lettuce-core Show documentation
Show all versions of lettuce-core Show documentation
Advanced and thread-safe Java Redis client for synchronous, asynchronous, and
reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs
and much more.
The newest version!
package io.lettuce.core.internal;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
/**
* Collection of utility methods to lookup {@link MethodHandle}s for default interface {@link Method}s. This class is part of
* the internal API and may change without further notice.
*
* @author Mark Paluch
* @since 4.4
*/
public class DefaultMethods {
private static final MethodHandleLookup methodHandleLookup = MethodHandleLookup.getMethodHandleLookup();
/**
* Lookup a {@link MethodHandle} for a default {@link Method}.
*
* @param method must be a {@link Method#isDefault() default} {@link Method}.
* @return the {@link MethodHandle}.
*/
public static MethodHandle lookupMethodHandle(Method method) throws ReflectiveOperationException {
LettuceAssert.notNull(method, "Method must not be null");
LettuceAssert.isTrue(method.isDefault(), "Method is not a default method");
return methodHandleLookup.lookup(method);
}
/**
* Strategies for {@link MethodHandle} lookup.
*/
enum MethodHandleLookup {
/**
* Open (via reflection construction of {@link Lookup}) method handle lookup. Works with Java 8 and with Java 9
* permitting illegal access.
*/
OPEN {
private final Optional> constructor = getLookupConstructor();
@Override
MethodHandle lookup(Method method) throws ReflectiveOperationException {
Constructor constructor = this.constructor
.orElseThrow(() -> new IllegalStateException("Could not obtain MethodHandles.lookup constructor"));
return constructor.newInstance(method.getDeclaringClass()).unreflectSpecial(method, method.getDeclaringClass());
}
@Override
boolean isAvailable() {
return constructor.isPresent();
}
},
/**
* Encapsulated {@link MethodHandle} lookup working on Java 9.
*/
ENCAPSULATED {
Method privateLookupIn = findBridgeMethod();
@Override
MethodHandle lookup(Method method) throws ReflectiveOperationException {
MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
return getLookup(method.getDeclaringClass()).findSpecial(method.getDeclaringClass(), method.getName(),
methodType, method.getDeclaringClass());
}
private Method findBridgeMethod() {
try {
return MethodHandles.class.getDeclaredMethod("privateLookupIn", Class.class, Lookup.class);
} catch (ReflectiveOperationException e) {
return null;
}
}
private Lookup getLookup(Class> declaringClass) {
Lookup lookup = MethodHandles.lookup();
if (privateLookupIn != null) {
try {
return (Lookup) privateLookupIn.invoke(null, declaringClass, lookup);
} catch (ReflectiveOperationException e) {
return lookup;
}
}
return lookup;
}
@Override
boolean isAvailable() {
return true;
}
};
/**
* Lookup a {@link MethodHandle} given {@link Method} to look up.
*
* @param method must not be {@code null}.
* @return the method handle.
* @throws ReflectiveOperationException
*/
abstract MethodHandle lookup(Method method) throws ReflectiveOperationException;
/**
* @return {@code true} if the lookup is available.
*/
abstract boolean isAvailable();
/**
* Obtain the first available {@link MethodHandleLookup}.
*
* @return the {@link MethodHandleLookup}
* @throws IllegalStateException if no {@link MethodHandleLookup} is available.
*/
public static MethodHandleLookup getMethodHandleLookup() {
return Arrays.stream(MethodHandleLookup.values()).filter(MethodHandleLookup::isAvailable).findFirst()
.orElseThrow(() -> new IllegalStateException("No MethodHandleLookup available!"));
}
private static Optional> getLookupConstructor() {
try {
Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return Optional.of(constructor);
} catch (Exception ex) {
// this is the signal that we are on Java 9 (encapsulated) and can't use the accessible constructor approach.
if (ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
return Optional.empty();
}
throw new IllegalStateException(ex);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy