Replace reflection usage with MethodHandles when performance matters (#10097)
Motivation: As we have java8 as a minimum target we can use MethodHandles. We should do so when we expect to have a method called multiple times. Modifications: - Replace usage of reflection with MethodHandles where it makes sense - Remove some code which was there to support java < 8 Result: Faster code
This commit is contained in:
parent
d639f55764
commit
fd0d06ee39
@ -20,7 +20,6 @@ import static java.util.Objects.requireNonNull;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.util.ByteProcessor;
|
import io.netty.util.ByteProcessor;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.zip.Adler32;
|
import java.util.zip.Adler32;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
@ -33,43 +32,34 @@ import java.util.zip.Checksum;
|
|||||||
* byte array ({@link ByteBuf#hasArray()} is {@code true}) or not.
|
* byte array ({@link ByteBuf#hasArray()} is {@code true}) or not.
|
||||||
*/
|
*/
|
||||||
abstract class ByteBufChecksum implements Checksum {
|
abstract class ByteBufChecksum implements Checksum {
|
||||||
private static final Method ADLER32_UPDATE_METHOD;
|
|
||||||
private static final Method CRC32_UPDATE_METHOD;
|
|
||||||
|
|
||||||
static {
|
|
||||||
// See if we can use fast-path when using ByteBuf that is not heap based as Adler32 and CRC32 added support
|
|
||||||
// for update(ByteBuffer) in JDK8.
|
|
||||||
ADLER32_UPDATE_METHOD = updateByteBuffer(new Adler32());
|
|
||||||
CRC32_UPDATE_METHOD = updateByteBuffer(new CRC32());
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ByteProcessor updateProcessor = value -> {
|
private final ByteProcessor updateProcessor = value -> {
|
||||||
update(value);
|
update(value);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Method updateByteBuffer(Checksum checksum) {
|
|
||||||
try {
|
|
||||||
Method method = checksum.getClass().getDeclaredMethod("update", ByteBuffer.class);
|
|
||||||
method.invoke(checksum, ByteBuffer.allocate(1));
|
|
||||||
return method;
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ByteBufChecksum wrapChecksum(Checksum checksum) {
|
static ByteBufChecksum wrapChecksum(Checksum checksum) {
|
||||||
requireNonNull(checksum, "checksum");
|
requireNonNull(checksum, "checksum");
|
||||||
if (checksum instanceof ByteBufChecksum) {
|
if (checksum instanceof ByteBufChecksum) {
|
||||||
return (ByteBufChecksum) checksum;
|
return (ByteBufChecksum) checksum;
|
||||||
}
|
}
|
||||||
if (checksum instanceof Adler32 && ADLER32_UPDATE_METHOD != null) {
|
if (checksum instanceof Adler32) {
|
||||||
return new ReflectiveByteBufChecksum(checksum, ADLER32_UPDATE_METHOD);
|
return new OptimizedByteBufChecksum<Adler32>((Adler32) checksum) {
|
||||||
|
@Override
|
||||||
|
public void update(ByteBuffer b) {
|
||||||
|
checksum.update(b);
|
||||||
}
|
}
|
||||||
if (checksum instanceof CRC32 && CRC32_UPDATE_METHOD != null) {
|
};
|
||||||
return new ReflectiveByteBufChecksum(checksum, CRC32_UPDATE_METHOD);
|
|
||||||
}
|
}
|
||||||
return new SlowByteBufChecksum(checksum);
|
if (checksum instanceof CRC32) {
|
||||||
|
return new OptimizedByteBufChecksum<CRC32>((CRC32) checksum) {
|
||||||
|
@Override
|
||||||
|
public void update(ByteBuffer b) {
|
||||||
|
checksum.update(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new SlowByteBufChecksum<>(checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,12 +73,9 @@ abstract class ByteBufChecksum implements Checksum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ReflectiveByteBufChecksum extends SlowByteBufChecksum {
|
private abstract static class OptimizedByteBufChecksum<T extends Checksum> extends SlowByteBufChecksum<T> {
|
||||||
private final Method method;
|
OptimizedByteBufChecksum(T checksum) {
|
||||||
|
|
||||||
ReflectiveByteBufChecksum(Checksum checksum, Method method) {
|
|
||||||
super(checksum);
|
super(checksum);
|
||||||
this.method = method;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -97,19 +84,21 @@ abstract class ByteBufChecksum implements Checksum {
|
|||||||
update(b.array(), b.arrayOffset() + off, len);
|
update(b.array(), b.arrayOffset() + off, len);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
method.invoke(checksum, CompressionUtil.safeNioBuffer(b, off, len));
|
update(CompressionUtil.safeNioBuffer(b, off, len));
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void update(ByteBuffer b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SlowByteBufChecksum extends ByteBufChecksum {
|
private static class SlowByteBufChecksum<T extends Checksum> extends ByteBufChecksum {
|
||||||
|
|
||||||
protected final Checksum checksum;
|
protected final T checksum;
|
||||||
|
|
||||||
SlowByteBufChecksum(Checksum checksum) {
|
SlowByteBufChecksum(T checksum) {
|
||||||
this.checksum = checksum;
|
this.checksum = checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@ package io.netty.util.internal;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
@ -33,15 +35,15 @@ import java.security.PrivilegedAction;
|
|||||||
*/
|
*/
|
||||||
final class CleanerJava6 implements Cleaner {
|
final class CleanerJava6 implements Cleaner {
|
||||||
private static final long CLEANER_FIELD_OFFSET;
|
private static final long CLEANER_FIELD_OFFSET;
|
||||||
private static final Method CLEAN_METHOD;
|
private static final MethodHandle CLEAN_HANDLE;
|
||||||
private static final Field CLEANER_FIELD;
|
private static final MethodHandle CLEANER_FIELD_HANDLE;
|
||||||
|
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
long fieldOffset;
|
long fieldOffset;
|
||||||
Method clean;
|
MethodHandle cleanHandle = null;
|
||||||
Field cleanerField;
|
MethodHandle cleanerFieldHandle = null;
|
||||||
Throwable error = null;
|
Throwable error = null;
|
||||||
final ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
final ByteBuffer direct = ByteBuffer.allocateDirect(1);
|
||||||
try {
|
try {
|
||||||
@ -62,7 +64,8 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
throw (Throwable) mayBeCleanerField;
|
throw (Throwable) mayBeCleanerField;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanerField = (Field) mayBeCleanerField;
|
Field cleanerField = (Field) mayBeCleanerField;
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
|
||||||
final Object cleaner;
|
final Object cleaner;
|
||||||
|
|
||||||
@ -74,15 +77,16 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
} else {
|
} else {
|
||||||
fieldOffset = -1;
|
fieldOffset = -1;
|
||||||
cleaner = cleanerField.get(direct);
|
cleaner = cleanerField.get(direct);
|
||||||
|
cleanerFieldHandle = lookup.unreflectGetter(cleanerField);
|
||||||
}
|
}
|
||||||
clean = cleaner.getClass().getDeclaredMethod("clean");
|
cleanHandle = lookup.findVirtual(cleaner.getClass(), "clean", MethodType.methodType(void.class));
|
||||||
clean.invoke(cleaner);
|
cleanHandle.invoke(cleaner);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// We don't have ByteBuffer.cleaner().
|
// We don't have ByteBuffer.cleaner().
|
||||||
fieldOffset = -1;
|
fieldOffset = -1;
|
||||||
clean = null;
|
cleanerFieldHandle = null;
|
||||||
|
cleanHandle = null;
|
||||||
error = t;
|
error = t;
|
||||||
cleanerField = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
@ -90,13 +94,13 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
} else {
|
} else {
|
||||||
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
||||||
}
|
}
|
||||||
CLEANER_FIELD = cleanerField;
|
CLEANER_FIELD_HANDLE = cleanerFieldHandle;
|
||||||
CLEANER_FIELD_OFFSET = fieldOffset;
|
CLEANER_FIELD_OFFSET = fieldOffset;
|
||||||
CLEAN_METHOD = clean;
|
CLEAN_HANDLE = cleanHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isSupported() {
|
static boolean isSupported() {
|
||||||
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD != null;
|
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD_HANDLE != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,17 +133,17 @@ final class CleanerJava6 implements Cleaner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void freeDirectBuffer0(ByteBuffer buffer) throws Exception {
|
private static void freeDirectBuffer0(ByteBuffer buffer) throws Throwable {
|
||||||
final Object cleaner;
|
final Object cleaner;
|
||||||
// If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use
|
// If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use
|
||||||
// sun.misc.Unsafe.
|
// sun.misc.Unsafe.
|
||||||
if (CLEANER_FIELD_OFFSET == -1) {
|
if (CLEANER_FIELD_OFFSET == -1) {
|
||||||
cleaner = CLEANER_FIELD.get(buffer);
|
cleaner = CLEANER_FIELD_HANDLE.invoke(buffer);
|
||||||
} else {
|
} else {
|
||||||
cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
|
cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
|
||||||
}
|
}
|
||||||
if (cleaner != null) {
|
if (cleaner != null) {
|
||||||
CLEAN_METHOD.invoke(cleaner);
|
CLEAN_HANDLE.invoke(cleaner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,10 @@ package io.netty.util.internal;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
@ -30,34 +32,40 @@ import java.security.PrivilegedAction;
|
|||||||
final class CleanerJava9 implements Cleaner {
|
final class CleanerJava9 implements Cleaner {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
|
||||||
|
|
||||||
private static final Method INVOKE_CLEANER;
|
private static final MethodHandle INVOKE_CLEANER_HANDLE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final Method method;
|
final MethodHandle invokeCleanerHandle;
|
||||||
final Throwable error;
|
final Throwable error;
|
||||||
if (PlatformDependent0.hasUnsafe()) {
|
if (PlatformDependent0.hasUnsafe()) {
|
||||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
|
final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
|
||||||
Object maybeInvokeMethod = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
Object maybeInvokeMethodHandle = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||||
try {
|
try {
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
// See https://bugs.openjdk.java.net/browse/JDK-8171377
|
// See https://bugs.openjdk.java.net/browse/JDK-8171377
|
||||||
Method m = PlatformDependent0.UNSAFE.getClass().getDeclaredMethod(
|
MethodHandle m = lookup.findVirtual(
|
||||||
"invokeCleaner", ByteBuffer.class);
|
PlatformDependent0.UNSAFE.getClass(),
|
||||||
m.invoke(PlatformDependent0.UNSAFE, buffer);
|
"invokeCleaner",
|
||||||
|
MethodType.methodType(void.class, ByteBuffer.class)).bindTo(PlatformDependent0.UNSAFE);
|
||||||
|
m.invokeExact(buffer);
|
||||||
return m;
|
return m;
|
||||||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
return e;
|
return e;
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
PlatformDependent0.throwException(cause);
|
||||||
|
return cause;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (maybeInvokeMethod instanceof Throwable) {
|
if (maybeInvokeMethodHandle instanceof Throwable) {
|
||||||
method = null;
|
invokeCleanerHandle = null;
|
||||||
error = (Throwable) maybeInvokeMethod;
|
error = (Throwable) maybeInvokeMethodHandle;
|
||||||
} else {
|
} else {
|
||||||
method = (Method) maybeInvokeMethod;
|
invokeCleanerHandle = (MethodHandle) maybeInvokeMethodHandle;
|
||||||
error = null;
|
error = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
method = null;
|
invokeCleanerHandle = null;
|
||||||
error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
|
error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
|
||||||
}
|
}
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
@ -65,11 +73,11 @@ final class CleanerJava9 implements Cleaner {
|
|||||||
} else {
|
} else {
|
||||||
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
|
||||||
}
|
}
|
||||||
INVOKE_CLEANER = method;
|
INVOKE_CLEANER_HANDLE = invokeCleanerHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isSupported() {
|
static boolean isSupported() {
|
||||||
return INVOKE_CLEANER != null;
|
return INVOKE_CLEANER_HANDLE != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -78,7 +86,7 @@ final class CleanerJava9 implements Cleaner {
|
|||||||
// See https://bugs.openjdk.java.net/browse/JDK-8191053.
|
// See https://bugs.openjdk.java.net/browse/JDK-8191053.
|
||||||
if (System.getSecurityManager() == null) {
|
if (System.getSecurityManager() == null) {
|
||||||
try {
|
try {
|
||||||
INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer);
|
INVOKE_CLEANER_HANDLE.invokeExact(buffer);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
PlatformDependent0.throwException(cause);
|
PlatformDependent0.throwException(cause);
|
||||||
}
|
}
|
||||||
@ -90,9 +98,11 @@ final class CleanerJava9 implements Cleaner {
|
|||||||
private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
|
private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
|
||||||
Exception error = AccessController.doPrivileged((PrivilegedAction<Exception>) () -> {
|
Exception error = AccessController.doPrivileged((PrivilegedAction<Exception>) () -> {
|
||||||
try {
|
try {
|
||||||
INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer);
|
INVOKE_CLEANER_HANDLE.invokeExact(PlatformDependent0.UNSAFE, buffer);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
return e;
|
return e;
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
PlatformDependent.throwException(cause);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,9 @@ import io.netty.util.internal.logging.InternalLogger;
|
|||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -38,14 +41,13 @@ final class PlatformDependent0 {
|
|||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
|
||||||
private static final long ADDRESS_FIELD_OFFSET;
|
private static final long ADDRESS_FIELD_OFFSET;
|
||||||
private static final long BYTE_ARRAY_BASE_OFFSET;
|
private static final long BYTE_ARRAY_BASE_OFFSET;
|
||||||
private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
|
private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR_HANDLE;
|
||||||
private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
|
private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
|
||||||
private static final Method ALLOCATE_ARRAY_METHOD;
|
private static final MethodHandle ALLOCATE_ARRAY_HANDLE;
|
||||||
private static final int JAVA_VERSION = javaVersion0();
|
private static final int JAVA_VERSION = javaVersion0();
|
||||||
private static final boolean IS_ANDROID = isAndroid0();
|
private static final boolean IS_ANDROID = isAndroid0();
|
||||||
|
|
||||||
private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
|
private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
|
||||||
private static final Object INTERNAL_UNSAFE;
|
|
||||||
private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
|
private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
|
||||||
|
|
||||||
static final Unsafe UNSAFE;
|
static final Unsafe UNSAFE;
|
||||||
@ -66,16 +68,14 @@ final class PlatformDependent0 {
|
|||||||
static {
|
static {
|
||||||
final ByteBuffer direct;
|
final ByteBuffer direct;
|
||||||
Field addressField = null;
|
Field addressField = null;
|
||||||
Method allocateArrayMethod = null;
|
MethodHandle allocateArrayHandle = null;
|
||||||
Throwable unsafeUnavailabilityCause = null;
|
Throwable unsafeUnavailabilityCause;
|
||||||
Unsafe unsafe;
|
Unsafe unsafe;
|
||||||
Object internalUnsafe = null;
|
|
||||||
|
|
||||||
if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
|
if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
|
||||||
direct = null;
|
direct = null;
|
||||||
addressField = null;
|
addressField = null;
|
||||||
unsafe = null;
|
unsafe = null;
|
||||||
internalUnsafe = null;
|
|
||||||
} else {
|
} else {
|
||||||
direct = ByteBuffer.allocateDirect(1);
|
direct = ByteBuffer.allocateDirect(1);
|
||||||
|
|
||||||
@ -189,13 +189,13 @@ final class PlatformDependent0 {
|
|||||||
ADDRESS_FIELD_OFFSET = -1;
|
ADDRESS_FIELD_OFFSET = -1;
|
||||||
BYTE_ARRAY_BASE_OFFSET = -1;
|
BYTE_ARRAY_BASE_OFFSET = -1;
|
||||||
UNALIGNED = false;
|
UNALIGNED = false;
|
||||||
DIRECT_BUFFER_CONSTRUCTOR = null;
|
DIRECT_BUFFER_CONSTRUCTOR_HANDLE = null;
|
||||||
ALLOCATE_ARRAY_METHOD = null;
|
ALLOCATE_ARRAY_HANDLE = null;
|
||||||
} else {
|
} else {
|
||||||
Constructor<?> directBufferConstructor;
|
MethodHandle directBufferConstructorHandle;
|
||||||
long address = -1;
|
long address = -1;
|
||||||
try {
|
try {
|
||||||
final Object maybeDirectBufferConstructor =
|
final Object maybeDirectBufferConstructorHandle =
|
||||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||||
try {
|
try {
|
||||||
final Constructor<?> constructor =
|
final Constructor<?> constructor =
|
||||||
@ -204,34 +204,34 @@ final class PlatformDependent0 {
|
|||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
return cause;
|
return cause;
|
||||||
}
|
}
|
||||||
return constructor;
|
return MethodHandles.lookup().unreflectConstructor(constructor);
|
||||||
} catch (NoSuchMethodException | SecurityException e) {
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (maybeDirectBufferConstructor instanceof Constructor<?>) {
|
if (maybeDirectBufferConstructorHandle instanceof MethodHandle) {
|
||||||
address = UNSAFE.allocateMemory(1);
|
address = UNSAFE.allocateMemory(1);
|
||||||
// try to use the constructor now
|
// try to use the constructor now
|
||||||
try {
|
try {
|
||||||
((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
|
directBufferConstructorHandle = (MethodHandle) maybeDirectBufferConstructorHandle;
|
||||||
directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
|
directBufferConstructorHandle.invoke(address, 1);
|
||||||
logger.debug("direct buffer constructor: available");
|
logger.debug("direct buffer constructor: available");
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (Throwable ignore) {
|
||||||
directBufferConstructor = null;
|
directBufferConstructorHandle = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"direct buffer constructor: unavailable",
|
"direct buffer constructor: unavailable",
|
||||||
(Throwable) maybeDirectBufferConstructor);
|
(Throwable) maybeDirectBufferConstructorHandle);
|
||||||
directBufferConstructor = null;
|
directBufferConstructorHandle = null;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (address != -1) {
|
if (address != -1) {
|
||||||
UNSAFE.freeMemory(address);
|
UNSAFE.freeMemory(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
|
DIRECT_BUFFER_CONSTRUCTOR_HANDLE = directBufferConstructorHandle;
|
||||||
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
|
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
|
||||||
BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
|
BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
|
||||||
final boolean unaligned;
|
final boolean unaligned;
|
||||||
@ -300,24 +300,25 @@ final class PlatformDependent0 {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!(maybeException instanceof Throwable)) {
|
if (!(maybeException instanceof Throwable)) {
|
||||||
internalUnsafe = maybeException;
|
final Object finalInternalUnsafe = maybeException;
|
||||||
final Object finalInternalUnsafe = internalUnsafe;
|
|
||||||
maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||||
try {
|
try {
|
||||||
return finalInternalUnsafe.getClass().getDeclaredMethod(
|
return MethodHandles.lookup().findVirtual(finalInternalUnsafe.getClass(),
|
||||||
"allocateUninitializedArray", Class.class, int.class);
|
"allocateUninitializedArray",
|
||||||
} catch (NoSuchMethodException | SecurityException e) {
|
MethodType.methodType(byte[].class, Class.class, int.class))
|
||||||
|
.bindTo(finalInternalUnsafe);
|
||||||
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (maybeException instanceof Method) {
|
if (maybeException instanceof MethodHandle) {
|
||||||
try {
|
try {
|
||||||
Method m = (Method) maybeException;
|
MethodHandle m = (MethodHandle) maybeException;
|
||||||
byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
|
byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
|
||||||
assert bytes.length == 8;
|
assert bytes.length == 8;
|
||||||
allocateArrayMethod = m;
|
allocateArrayHandle = m;
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (Throwable e) {
|
||||||
maybeException = e;
|
maybeException = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,13 +333,11 @@ final class PlatformDependent0 {
|
|||||||
} else {
|
} else {
|
||||||
logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
|
logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
|
||||||
}
|
}
|
||||||
ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
|
ALLOCATE_ARRAY_HANDLE = allocateArrayHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL_UNSAFE = internalUnsafe;
|
|
||||||
|
|
||||||
logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
|
logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
|
||||||
DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
|
DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null ? "available" : "unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isExplicitNoUnsafe() {
|
static boolean isExplicitNoUnsafe() {
|
||||||
@ -387,7 +386,7 @@ final class PlatformDependent0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean hasDirectBufferNoCleanerConstructor() {
|
static boolean hasDirectBufferNoCleanerConstructor() {
|
||||||
return DIRECT_BUFFER_CONSTRUCTOR != null;
|
return DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
|
static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
|
||||||
@ -402,13 +401,16 @@ final class PlatformDependent0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean hasAllocateArrayMethod() {
|
static boolean hasAllocateArrayMethod() {
|
||||||
return ALLOCATE_ARRAY_METHOD != null;
|
return ALLOCATE_ARRAY_HANDLE != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] allocateUninitializedArray(int size) {
|
static byte[] allocateUninitializedArray(int size) {
|
||||||
try {
|
try {
|
||||||
return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size);
|
return (byte[]) ALLOCATE_ARRAY_HANDLE.invoke(byte.class, size);
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (Throwable e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw (Error) e;
|
||||||
|
}
|
||||||
throw new Error(e);
|
throw new Error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -417,7 +419,7 @@ final class PlatformDependent0 {
|
|||||||
ObjectUtil.checkPositiveOrZero(capacity, "capacity");
|
ObjectUtil.checkPositiveOrZero(capacity, "capacity");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
|
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR_HANDLE.invoke(address, capacity);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
// Not expected to ever throw!
|
// Not expected to ever throw!
|
||||||
if (cause instanceof Error) {
|
if (cause instanceof Error) {
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains methods that can be used to detect if conscrypt is usable.
|
* Contains methods that can be used to detect if conscrypt is usable.
|
||||||
@ -25,14 +26,17 @@ import java.lang.reflect.Method;
|
|||||||
final class Conscrypt {
|
final class Conscrypt {
|
||||||
// This class exists to avoid loading other conscrypt related classes using features only available in JDK8+,
|
// This class exists to avoid loading other conscrypt related classes using features only available in JDK8+,
|
||||||
// because we need to maintain JDK6+ runtime compatibility.
|
// because we need to maintain JDK6+ runtime compatibility.
|
||||||
private static final Method IS_CONSCRYPT_SSLENGINE = loadIsConscryptEngine();
|
private static final MethodHandle IS_CONSCRYPT_SSLENGINE = loadIsConscryptEngine();
|
||||||
private static final boolean CAN_INSTANCE_PROVIDER = canInstanceProvider();
|
private static final boolean CAN_INSTANCE_PROVIDER = canInstanceProvider();
|
||||||
|
|
||||||
private static Method loadIsConscryptEngine() {
|
private static MethodHandle loadIsConscryptEngine() {
|
||||||
try {
|
try {
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
|
||||||
Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt", true,
|
Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt", true,
|
||||||
ConscryptAlpnSslEngine.class.getClassLoader());
|
ConscryptAlpnSslEngine.class.getClassLoader());
|
||||||
return conscryptClass.getMethod("isConscrypt", SSLEngine.class);
|
return lookup.findStatic(conscryptClass, "isConscrypt",
|
||||||
|
MethodType.methodType(boolean.class, SSLEngine.class));
|
||||||
} catch (Throwable ignore) {
|
} catch (Throwable ignore) {
|
||||||
// Conscrypt was not loaded.
|
// Conscrypt was not loaded.
|
||||||
return null;
|
return null;
|
||||||
@ -43,7 +47,8 @@ final class Conscrypt {
|
|||||||
try {
|
try {
|
||||||
Class<?> providerClass = Class.forName("org.conscrypt.OpenSSLProvider", true,
|
Class<?> providerClass = Class.forName("org.conscrypt.OpenSSLProvider", true,
|
||||||
ConscryptAlpnSslEngine.class.getClassLoader());
|
ConscryptAlpnSslEngine.class.getClassLoader());
|
||||||
providerClass.newInstance();
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
lookup.findConstructor(providerClass, MethodType.methodType(void.class)).invoke();
|
||||||
return true;
|
return true;
|
||||||
} catch (Throwable ignore) {
|
} catch (Throwable ignore) {
|
||||||
return false;
|
return false;
|
||||||
@ -62,14 +67,17 @@ final class Conscrypt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isConscryptEngine(SSLEngine engine) {
|
private static boolean isConscryptEngine(SSLEngine engine) {
|
||||||
|
if (IS_CONSCRYPT_SSLENGINE != null) {
|
||||||
try {
|
try {
|
||||||
return (Boolean) IS_CONSCRYPT_SSLENGINE.invoke(null, engine);
|
return (boolean) IS_CONSCRYPT_SSLENGINE.invokeExact(engine);
|
||||||
} catch (IllegalAccessException ignore) {
|
} catch (IllegalAccessException ignore) {
|
||||||
return false;
|
return false;
|
||||||
} catch (InvocationTargetException ex) {
|
} catch (Throwable cause) {
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private Conscrypt() { }
|
private Conscrypt() { }
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ package io.netty.handler.ssl;
|
|||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,44 +34,52 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
|
|||||||
|
|
||||||
final class Java9SslUtils {
|
final class Java9SslUtils {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Java9SslUtils.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Java9SslUtils.class);
|
||||||
private static final Method SET_APPLICATION_PROTOCOLS;
|
private static final MethodHandle SET_APPLICATION_PROTOCOLS;
|
||||||
private static final Method GET_APPLICATION_PROTOCOL;
|
private static final MethodHandle GET_APPLICATION_PROTOCOL;
|
||||||
private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL;
|
private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL;
|
||||||
private static final Method SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
|
private static final MethodHandle SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
|
||||||
private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
|
private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Method getHandshakeApplicationProtocol = null;
|
MethodHandle getHandshakeApplicationProtocol;
|
||||||
Method getApplicationProtocol = null;
|
MethodHandle getApplicationProtocol;
|
||||||
Method setApplicationProtocols = null;
|
MethodHandle setApplicationProtocols;
|
||||||
Method setHandshakeApplicationProtocolSelector = null;
|
MethodHandle setHandshakeApplicationProtocolSelector;
|
||||||
Method getHandshakeApplicationProtocolSelector = null;
|
MethodHandle getHandshakeApplicationProtocolSelector;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL);
|
SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL);
|
||||||
context.init(null, null, null);
|
context.init(null, null, null);
|
||||||
SSLEngine engine = context.createSSLEngine();
|
SSLEngine engine = context.createSSLEngine();
|
||||||
getHandshakeApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
SSLEngine.class.getMethod("getHandshakeApplicationProtocol"));
|
getHandshakeApplicationProtocol =
|
||||||
getHandshakeApplicationProtocol.invoke(engine);
|
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
|
||||||
getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
|
lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocol",
|
||||||
SSLEngine.class.getMethod("getApplicationProtocol"));
|
MethodType.methodType(String.class)));
|
||||||
getApplicationProtocol.invoke(engine);
|
getHandshakeApplicationProtocol.invokeExact(engine);
|
||||||
|
|
||||||
setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
|
getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
|
||||||
SSLParameters.class.getMethod("setApplicationProtocols", String[].class));
|
lookup.findVirtual(SSLEngine.class, "getApplicationProtocol",
|
||||||
setApplicationProtocols.invoke(engine.getSSLParameters(), new Object[]{EmptyArrays.EMPTY_STRINGS});
|
MethodType.methodType(String.class)));
|
||||||
|
getApplicationProtocol.invokeExact(engine);
|
||||||
|
|
||||||
|
setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
|
||||||
|
lookup.findVirtual(SSLParameters.class, "setApplicationProtocols",
|
||||||
|
MethodType.methodType(void.class, String[].class)));
|
||||||
|
setApplicationProtocols.invokeExact(engine.getSSLParameters(), EmptyArrays.EMPTY_STRINGS);
|
||||||
|
|
||||||
setHandshakeApplicationProtocolSelector =
|
setHandshakeApplicationProtocolSelector =
|
||||||
AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
|
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
|
||||||
SSLEngine.class.getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class));
|
lookup.findVirtual(SSLEngine.class, "setHandshakeApplicationProtocolSelector",
|
||||||
setHandshakeApplicationProtocolSelector.invoke(engine,
|
MethodType.methodType(void.class, BiFunction.class)));
|
||||||
|
setHandshakeApplicationProtocolSelector.invokeExact(engine,
|
||||||
(BiFunction<SSLEngine, List<String>, String>) (sslEngine, strings) -> null);
|
(BiFunction<SSLEngine, List<String>, String>) (sslEngine, strings) -> null);
|
||||||
|
|
||||||
getHandshakeApplicationProtocolSelector =
|
getHandshakeApplicationProtocolSelector =
|
||||||
AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
|
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
|
||||||
SSLEngine.class.getMethod("getHandshakeApplicationProtocolSelector"));
|
lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocolSelector",
|
||||||
getHandshakeApplicationProtocolSelector.invoke(engine);
|
MethodType.methodType(BiFunction.class)));
|
||||||
|
getHandshakeApplicationProtocolSelector.invokeExact(engine);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.error("Unable to initialize Java9SslUtils, but the detected javaVersion was: {}",
|
logger.error("Unable to initialize Java9SslUtils, but the detected javaVersion was: {}",
|
||||||
PlatformDependent.javaVersion(), t);
|
PlatformDependent.javaVersion(), t);
|
||||||
@ -95,20 +105,20 @@ final class Java9SslUtils {
|
|||||||
|
|
||||||
static String getApplicationProtocol(SSLEngine sslEngine) {
|
static String getApplicationProtocol(SSLEngine sslEngine) {
|
||||||
try {
|
try {
|
||||||
return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine);
|
return (String) GET_APPLICATION_PROTOCOL.invokeExact(sslEngine);
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
|
static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
|
||||||
try {
|
try {
|
||||||
return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine);
|
return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invokeExact(sslEngine);
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,10 +128,10 @@ final class Java9SslUtils {
|
|||||||
|
|
||||||
String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
|
String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
|
||||||
try {
|
try {
|
||||||
SET_APPLICATION_PROTOCOLS.invoke(parameters, new Object[]{protocolArray});
|
SET_APPLICATION_PROTOCOLS.invokeExact(parameters, protocolArray);
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
engine.setSSLParameters(parameters);
|
engine.setSSLParameters(parameters);
|
||||||
@ -130,21 +140,22 @@ final class Java9SslUtils {
|
|||||||
static void setHandshakeApplicationProtocolSelector(
|
static void setHandshakeApplicationProtocolSelector(
|
||||||
SSLEngine engine, BiFunction<SSLEngine, List<String>, String> selector) {
|
SSLEngine engine, BiFunction<SSLEngine, List<String>, String> selector) {
|
||||||
try {
|
try {
|
||||||
SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selector);
|
SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine, selector);
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
|
static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
|
||||||
try {
|
try {
|
||||||
return (BiFunction<SSLEngine, List<String>, String>)
|
return (BiFunction<SSLEngine, List<String>, String>)
|
||||||
GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine);
|
GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine);
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
pom.xml
5
pom.xml
@ -810,6 +810,11 @@
|
|||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
</signature>
|
</signature>
|
||||||
<ignores>
|
<ignores>
|
||||||
|
<!--
|
||||||
|
We need to whitelist MethodHandle as animal-sniffer can not handle it correctly
|
||||||
|
https://github.com/mojohaus/animal-sniffer/issues/67
|
||||||
|
-->
|
||||||
|
<ignore>java.lang.invoke.MethodHandle</ignore>
|
||||||
<ignore>sun.misc.Unsafe</ignore>
|
<ignore>sun.misc.Unsafe</ignore>
|
||||||
<ignore>sun.misc.Cleaner</ignore>
|
<ignore>sun.misc.Cleaner</ignore>
|
||||||
<ignore>sun.nio.ch.DirectBuffer</ignore>
|
<ignore>sun.nio.ch.DirectBuffer</ignore>
|
||||||
|
@ -19,7 +19,9 @@ import io.netty.util.internal.PlatformDependent;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
@ -34,10 +36,10 @@ public final class DnsServerAddressStreamProviders {
|
|||||||
|
|
||||||
private static final InternalLogger LOGGER =
|
private static final InternalLogger LOGGER =
|
||||||
InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class);
|
InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class);
|
||||||
private static final Constructor<? extends DnsServerAddressStreamProvider> STREAM_PROVIDER_CONSTRUCTOR;
|
private static final MethodHandle STREAM_PROVIDER_CONSTRUCTOR_HANDLE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Constructor<? extends DnsServerAddressStreamProvider> constructor = null;
|
MethodHandle constructorHandle = null;
|
||||||
if (PlatformDependent.isOsx()) {
|
if (PlatformDependent.isOsx()) {
|
||||||
try {
|
try {
|
||||||
// As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar
|
// As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar
|
||||||
@ -56,20 +58,22 @@ public final class DnsServerAddressStreamProviders {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Class<? extends DnsServerAddressStreamProvider> providerClass =
|
Class<? extends DnsServerAddressStreamProvider> providerClass =
|
||||||
(Class<? extends DnsServerAddressStreamProvider>) maybeProvider;
|
(Class<? extends DnsServerAddressStreamProvider>) maybeProvider;
|
||||||
|
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
Method method = providerClass.getMethod("ensureAvailability");
|
Method method = providerClass.getMethod("ensureAvailability");
|
||||||
method.invoke(null);
|
method.invoke(null);
|
||||||
constructor = providerClass.getConstructor();
|
constructorHandle = lookup.findConstructor(providerClass, MethodType.methodType(void.class));
|
||||||
constructor.newInstance();
|
constructorHandle.invoke();
|
||||||
} else if (!(maybeProvider instanceof ClassNotFoundException)) {
|
} else if (!(maybeProvider instanceof ClassNotFoundException)) {
|
||||||
throw (Throwable) maybeProvider;
|
throw (Throwable) maybeProvider;
|
||||||
}
|
}
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Unable to use MacOSDnsServerAddressStreamProvider, fallback to system defaults", cause);
|
"Unable to use MacOSDnsServerAddressStreamProvider, fallback to system defaults", cause);
|
||||||
constructor = null;
|
constructorHandle = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STREAM_PROVIDER_CONSTRUCTOR = constructor;
|
STREAM_PROVIDER_CONSTRUCTOR_HANDLE = constructorHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DnsServerAddressStreamProviders() {
|
private DnsServerAddressStreamProviders() {
|
||||||
@ -83,11 +87,13 @@ public final class DnsServerAddressStreamProviders {
|
|||||||
* configuration.
|
* configuration.
|
||||||
*/
|
*/
|
||||||
public static DnsServerAddressStreamProvider platformDefault() {
|
public static DnsServerAddressStreamProvider platformDefault() {
|
||||||
if (STREAM_PROVIDER_CONSTRUCTOR != null) {
|
if (STREAM_PROVIDER_CONSTRUCTOR_HANDLE != null) {
|
||||||
try {
|
try {
|
||||||
return STREAM_PROVIDER_CONSTRUCTOR.newInstance();
|
return (DnsServerAddressStreamProvider) STREAM_PROVIDER_CONSTRUCTOR_HANDLE.invoke();
|
||||||
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||||
// ignore
|
// ignore
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
PlatformDependent.throwException(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return unixDefault();
|
return unixDefault();
|
||||||
|
Binary file not shown.
@ -19,13 +19,13 @@ import io.netty.channel.ChannelException;
|
|||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
import io.netty.channel.socket.DatagramChannelConfig;
|
import io.netty.channel.socket.DatagramChannelConfig;
|
||||||
import io.netty.channel.socket.DefaultDatagramChannelConfig;
|
import io.netty.channel.socket.DefaultDatagramChannelConfig;
|
||||||
import io.netty.util.internal.PlatformDependent;
|
|
||||||
import io.netty.util.internal.SocketUtils;
|
import io.netty.util.internal.SocketUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.net.StandardSocketOptions;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -35,82 +35,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
|
class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
|
||||||
|
|
||||||
private static final Object IP_MULTICAST_TTL;
|
|
||||||
private static final Object IP_MULTICAST_IF;
|
|
||||||
private static final Object IP_MULTICAST_LOOP;
|
|
||||||
private static final Method GET_OPTION;
|
|
||||||
private static final Method SET_OPTION;
|
|
||||||
|
|
||||||
static {
|
|
||||||
ClassLoader classLoader = PlatformDependent.getClassLoader(DatagramChannel.class);
|
|
||||||
Class<?> socketOptionType = null;
|
|
||||||
try {
|
|
||||||
socketOptionType = Class.forName("java.net.SocketOption", true, classLoader);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Not Java 7+
|
|
||||||
}
|
|
||||||
Class<?> stdSocketOptionType = null;
|
|
||||||
try {
|
|
||||||
stdSocketOptionType = Class.forName("java.net.StandardSocketOptions", true, classLoader);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Not Java 7+
|
|
||||||
}
|
|
||||||
|
|
||||||
Object ipMulticastTtl = null;
|
|
||||||
Object ipMulticastIf = null;
|
|
||||||
Object ipMulticastLoop = null;
|
|
||||||
Method getOption = null;
|
|
||||||
Method setOption = null;
|
|
||||||
if (socketOptionType != null) {
|
|
||||||
try {
|
|
||||||
ipMulticastTtl = stdSocketOptionType.getDeclaredField("IP_MULTICAST_TTL").get(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error("cannot locate the IP_MULTICAST_TTL field", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ipMulticastIf = stdSocketOptionType.getDeclaredField("IP_MULTICAST_IF").get(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error("cannot locate the IP_MULTICAST_IF field", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ipMulticastLoop = stdSocketOptionType.getDeclaredField("IP_MULTICAST_LOOP").get(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error("cannot locate the IP_MULTICAST_LOOP field", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> networkChannelClass = null;
|
|
||||||
try {
|
|
||||||
networkChannelClass = Class.forName("java.nio.channels.NetworkChannel", true, classLoader);
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
// Not Java 7+
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkChannelClass == null) {
|
|
||||||
getOption = null;
|
|
||||||
setOption = null;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
getOption = networkChannelClass.getDeclaredMethod("getOption", socketOptionType);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error("cannot locate the getOption() method", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setOption = networkChannelClass.getDeclaredMethod("setOption", socketOptionType, Object.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error("cannot locate the setOption() method", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IP_MULTICAST_TTL = ipMulticastTtl;
|
|
||||||
IP_MULTICAST_IF = ipMulticastIf;
|
|
||||||
IP_MULTICAST_LOOP = ipMulticastLoop;
|
|
||||||
GET_OPTION = getOption;
|
|
||||||
SET_OPTION = setOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DatagramChannel javaChannel;
|
private final DatagramChannel javaChannel;
|
||||||
|
|
||||||
NioDatagramChannelConfig(NioDatagramChannel channel, DatagramChannel javaChannel) {
|
NioDatagramChannelConfig(NioDatagramChannel channel, DatagramChannel javaChannel) {
|
||||||
@ -120,13 +44,21 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTimeToLive() {
|
public int getTimeToLive() {
|
||||||
return (Integer) getOption0(IP_MULTICAST_TTL);
|
try {
|
||||||
|
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatagramChannelConfig setTimeToLive(int ttl) {
|
public DatagramChannelConfig setTimeToLive(int ttl) {
|
||||||
setOption0(IP_MULTICAST_TTL, ttl);
|
try {
|
||||||
|
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl);
|
||||||
return this;
|
return this;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -153,24 +85,40 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkInterface getNetworkInterface() {
|
public NetworkInterface getNetworkInterface() {
|
||||||
return (NetworkInterface) getOption0(IP_MULTICAST_IF);
|
try {
|
||||||
|
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_IF);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
|
public DatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
|
||||||
setOption0(IP_MULTICAST_IF, networkInterface);
|
try {
|
||||||
|
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
|
||||||
return this;
|
return this;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLoopbackModeDisabled() {
|
public boolean isLoopbackModeDisabled() {
|
||||||
return (Boolean) getOption0(IP_MULTICAST_LOOP);
|
try {
|
||||||
|
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_LOOP);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
|
public DatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
|
||||||
setOption0(IP_MULTICAST_LOOP, loopbackModeDisabled);
|
try {
|
||||||
|
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, loopbackModeDisabled);
|
||||||
return this;
|
return this;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -184,30 +132,6 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
|
|||||||
((NioDatagramChannel) channel).clearReadPending0();
|
((NioDatagramChannel) channel).clearReadPending0();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getOption0(Object option) {
|
|
||||||
if (GET_OPTION == null) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return GET_OPTION.invoke(javaChannel, option);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ChannelException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setOption0(Object option, Object value) {
|
|
||||||
if (SET_OPTION == null) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
SET_OPTION.invoke(javaChannel, option, value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ChannelException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||||
if (option instanceof NioChannelOption) {
|
if (option instanceof NioChannelOption) {
|
||||||
|
Loading…
Reference in New Issue
Block a user