Ensure no java.lang.UnsupportedClassVersionError are thrown if running on Java7 and try to check if conscrypt is available.

Motivation:

We need to ensure we not try to load any conscrypt classes directly (which means without using reflection) in the same class that is used to check if conscrypt is available. This is needed as otherwise we will have the following problem when try to use netty on java7:

java.lang.UnsupportedClassVersionError: org/conscrypt/BufferAllocator : Unsupported major.minor version 52.0
	at io.netty.handler.ssl.ConscryptJdkSslEngineInteropTest.checkConscrypt(ConscryptJdkSslEngineInteropTest.java:49)

This regression was introduced by 4448b8f42f and detected on the CI when using:

mvn clean package -DtestJavaHome=$JAVA7_HOME

Modifications:

Move the detection code in an extra class and use it.

Result:

Works correctly also when using Java7.
This commit is contained in:
Norman Maurer 2017-08-04 08:06:36 +02:00
parent 4bb89dcc54
commit 32f497760f
6 changed files with 76 additions and 47 deletions

View File

@ -0,0 +1,70 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import io.netty.util.internal.PlatformDependent;
import javax.net.ssl.SSLEngine;
import java.lang.reflect.Method;
/**
* Contains methods that can be used to detect if conscrypt is usable.
*/
final class Conscrypt {
// This class exists to avoid loading other conscrypt related classes using features only available in JDK8+,
// because we need to maintain JDK6+ runtime compatibility.
private static final Class<?> ENGINES_CLASS = getEnginesClass();
/**
* Indicates whether or not conscrypt is available on the current system.
*/
static boolean isAvailable() {
return ENGINES_CLASS != null && PlatformDependent.javaVersion() >= 8;
}
static boolean isEngineSupported(SSLEngine engine) {
return isAvailable() && isConscryptEngine(engine, ENGINES_CLASS);
}
private static Class<?> getEnginesClass() {
try {
// Always use bootstrap class loader.
Class<?> engineClass = Class.forName("org.conscrypt.Conscrypt$Engines", true,
ConscryptAlpnSslEngine.class.getClassLoader());
// Ensure that it also has the isConscrypt method.
getIsConscryptMethod(engineClass);
return engineClass;
} catch (Throwable ignore) {
// Conscrypt was not loaded.
return null;
}
}
private static boolean isConscryptEngine(SSLEngine engine, Class<?> enginesClass) {
try {
Method method = getIsConscryptMethod(enginesClass);
return (Boolean) method.invoke(null, engine);
} catch (Throwable ignore) {
return false;
}
}
private static Method getIsConscryptMethod(Class<?> enginesClass) throws NoSuchMethodException {
return enginesClass.getMethod("isConscrypt", SSLEngine.class);
}
private Conscrypt() { }
}

View File

@ -23,7 +23,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -32,7 +31,6 @@ import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
import org.conscrypt.AllocatedBuffer;
import org.conscrypt.BufferAllocator;
@ -45,18 +43,6 @@ import org.conscrypt.HandshakeListener;
abstract class ConscryptAlpnSslEngine extends JdkSslEngine {
private static final boolean USE_BUFFER_ALLOCATOR = SystemPropertyUtil.getBoolean(
"io.netty.handler.ssl.conscrypt.useBufferAllocator", true);
private static final Class<?> ENGINES_CLASS = getEnginesClass();
/**
* Indicates whether or not conscrypt is available on the current system.
*/
static boolean isAvailable() {
return ENGINES_CLASS != null && PlatformDependent.javaVersion() >= 8;
}
static boolean isEngineSupported(SSLEngine engine) {
return isAvailable() && isConscryptEngine(engine, ENGINES_CLASS);
}
static ConscryptAlpnSslEngine newClientEngine(SSLEngine engine, ByteBufAllocator alloc,
JdkApplicationProtocolNegotiator applicationNegotiator) {
@ -168,33 +154,6 @@ abstract class ConscryptAlpnSslEngine extends JdkSslEngine {
}
}
private static Class<?> getEnginesClass() {
try {
// Always use bootstrap class loader.
Class<?> engineClass = Class.forName("org.conscrypt.Conscrypt$Engines", true,
ConscryptAlpnSslEngine.class.getClassLoader());
// Ensure that it also has the isConscrypt method.
getIsConscryptMethod(engineClass);
return engineClass;
} catch (Throwable ignore) {
// Conscrypt was not loaded.
return null;
}
}
private static boolean isConscryptEngine(SSLEngine engine, Class<?> enginesClass) {
try {
Method method = getIsConscryptMethod(enginesClass);
return (Boolean) method.invoke(null, engine);
} catch (Throwable ignore) {
return false;
}
}
private static Method getIsConscryptMethod(Class<?> enginesClass) throws NoSuchMethodException {
return enginesClass.getMethod("isConscrypt", SSLEngine.class);
}
private static final class BufferAllocatorAdapter extends BufferAllocator {
private final ByteBufAllocator alloc;
@ -214,7 +173,7 @@ abstract class ConscryptAlpnSslEngine extends JdkSslEngine {
BufferAdapter(ByteBuf nettyBuffer) {
this.nettyBuffer = nettyBuffer;
this.buffer = nettyBuffer.nioBuffer(0, nettyBuffer.capacity());
buffer = nettyBuffer.nioBuffer(0, nettyBuffer.capacity());
}
@Override

View File

@ -22,7 +22,7 @@ import javax.net.ssl.SSLEngine;
* The {@link JdkApplicationProtocolNegotiator} to use if you need ALPN and are using {@link SslProvider#JDK}.
*/
public final class JdkAlpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator {
private static final boolean AVAILABLE = ConscryptAlpnSslEngine.isAvailable() || JettyAlpnSslEngine.isAvailable();
private static final boolean AVAILABLE = Conscrypt.isAvailable() || JettyAlpnSslEngine.isAvailable();
private static final SslEngineWrapperFactory ALPN_WRAPPER = AVAILABLE ? new AlpnWrapper() : new FailureWrapper();
/**
@ -122,7 +122,7 @@ public final class JdkAlpnApplicationProtocolNegotiator extends JdkBaseApplicati
@Override
public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc,
JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) {
if (ConscryptAlpnSslEngine.isEngineSupported(engine)) {
if (Conscrypt.isEngineSupported(engine)) {
return isServer ? ConscryptAlpnSslEngine.newServerEngine(engine, alloc, applicationNegotiator)
: ConscryptAlpnSslEngine.newClientEngine(engine, alloc, applicationNegotiator);
}

View File

@ -46,7 +46,7 @@ public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest {
@BeforeClass
public static void checkConscrypt() {
assumeTrue(ConscryptAlpnSslEngine.isAvailable());
assumeTrue(Conscrypt.isAvailable());
}
@Override

View File

@ -46,7 +46,7 @@ public class JdkConscryptSslEngineInteropTest extends SSLEngineTest {
@BeforeClass
public static void checkConscrypt() {
assumeTrue(ConscryptAlpnSslEngine.isAvailable());
assumeTrue(Conscrypt.isAvailable());
}
@Override

View File

@ -81,7 +81,7 @@ public class JdkSslEngineTest extends SSLEngineTest {
@Override
boolean isAvailable() {
return ConscryptAlpnSslEngine.isAvailable();
return Conscrypt.isAvailable();
}
@Override