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:
Norman Maurer 2020-03-11 21:04:40 +01:00 committed by GitHub
parent d639f55764
commit fd0d06ee39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 232 additions and 273 deletions

View File

@ -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) { if (checksum instanceof CRC32) {
return new ReflectiveByteBufChecksum(checksum, CRC32_UPDATE_METHOD); return new OptimizedByteBufChecksum<CRC32>((CRC32) checksum) {
@Override
public void update(ByteBuffer b) {
checksum.update(b);
}
};
} }
return new SlowByteBufChecksum(checksum); 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;
} }

View File

@ -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);
} }
} }
} }

View File

@ -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;
}); });

View File

@ -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) {

View File

@ -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,13 +67,16 @@ final class Conscrypt {
} }
private static boolean isConscryptEngine(SSLEngine engine) { private static boolean isConscryptEngine(SSLEngine engine) {
try { if (IS_CONSCRYPT_SSLENGINE != null) {
return (Boolean) IS_CONSCRYPT_SSLENGINE.invoke(null, engine); try {
} catch (IllegalAccessException ignore) { return (boolean) IS_CONSCRYPT_SSLENGINE.invokeExact(engine);
return false; } catch (IllegalAccessException ignore) {
} catch (InvocationTargetException ex) { return false;
throw new RuntimeException(ex); } catch (Throwable cause) {
throw new RuntimeException(cause);
}
} }
return false;
} }
private Conscrypt() { } private Conscrypt() { }

View File

@ -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);
} }
} }

View File

@ -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>

View File

@ -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();

View File

@ -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 {
return this; javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl);
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 {
return this; javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
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 {
return this; javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, loopbackModeDisabled);
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) {