/*
 * 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<?> CONSCRYPT_CLASS = getConscryptClass();

    /**
     * Indicates whether or not conscrypt is available on the current system.
     */
    static boolean isAvailable() {
        return CONSCRYPT_CLASS != null && PlatformDependent.javaVersion() >= 8;
    }

    static boolean isEngineSupported(SSLEngine engine) {
        return isAvailable() && isConscryptEngine(engine, CONSCRYPT_CLASS);
    }

    private static Class<?> getConscryptClass() {
        try {
            Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt", true,
                    ConscryptAlpnSslEngine.class.getClassLoader());
            // Ensure that it also has the isConscrypt method.
            getIsConscryptMethod(conscryptClass);
            return conscryptClass;
        } catch (Throwable ignore) {
            // Conscrypt was not loaded.
            return null;
        }
    }

    private static boolean isConscryptEngine(SSLEngine engine, Class<?> conscryptClass) {
        try {
            Method method = getIsConscryptMethod(conscryptClass);
            return (Boolean) method.invoke(null, engine);
        } catch (Throwable ignore) {
            return false;
        }
    }

    private static Method getIsConscryptMethod(Class<?> conscryptClass) throws NoSuchMethodException {
        return conscryptClass.getMethod("isConscrypt", SSLEngine.class);
    }

    private Conscrypt() { }
}

@@ -741,16 +741,68 @@
 Engine;%0A
+import java.lang.reflect.InvocationTargetException;%0A
 import j
@@ -1137,411 +1137,64 @@
 nal 
-Class%3C?%3E CONSCRYPT_CLASS = getConscryptClass();%0A%0A    /**%0A     * Indicates whether or not conscrypt is available on the current system.%0A     */%0A    static boolean isAvailable() %7B%0A        return CONSCRYPT_CLASS != null && PlatformDependent.javaVersion() %3E= 8;%0A    %7D%0A%0A    static boolean isEngineSupported(SSLEngine engine) %7B%0A        return isAvailable() && isConscryptEngine(engine, CONSCRYPT_CLASS);%0A    %7D
+Method IS_CONSCRYPT_SSLENGINE = loadIsConscryptEngine();
 %0A%0A  
@@ -1214,20 +1214,21 @@
 tic 
-Class%3C?%3E get
+Method loadIs
 Cons
@@ -1232,21 +1232,22 @@
 onscrypt
-Class
+Engine
 () %7B%0A   
@@ -1426,92 +1426,40 @@
     
-// Ensure that it also has the isConscrypt method.%0A            getIsConscryp
+return conscryptClass.ge
 tMethod(
 cons
@@ -1450,33 +1450,36 @@
 s.getMethod(
-c
+%22isC
 onscrypt
 Class);%0A    
@@ -1470,49 +1470,27 @@
 rypt
-Class);%0A            return conscryptC
+%22, SSLEngine.c
 lass
+)
 ;%0A  
@@ -1615,173 +1615,428 @@
     
-private static boolean isConscryptEngine(SSLEngine engine, Class%3C?%3E conscryptClass) %7B%0A        try %7B%0A    
+/**%0A     * Indicates whether or not conscrypt is available on the current system.%0A     */%0A    static boolean isAvailable() %7B%0A        return IS_CONSCRYPT_SSLENGINE != null && PlatformDependent.javaVersion() %3E= 8;%0A    %7D%0A%0A    static boolean isEngineSupported(SSLEngine engine) %7B%0A        return isAvailable() && isConscryptEngine(engine);%0A
     
+%7D%0A%0A
     
-Method method = getIsConscryptMethod(conscryptClass);
+private static boolean isConscryptEngine(SSLEngine engine) %7B%0A        try %7B
 %0A   
@@ -2061,22 +2061,38 @@
 oolean) 
-method
+IS_CONSCRYPT_SSLENGINE
 .invoke(
@@ -2119,33 +2119,46 @@
    %7D catch (
-Throwable
+IllegalAccessException
  ignore) %7B%0A 
@@ -2195,107 +2195,32 @@
    %7D
-%0A    %7D%0A%0A    private static Method getIsConscryptMethod(Class%3C?%3E conscryptClass) throws NoSuchMethod
+ catch (InvocationTarget
 Exce
@@ -2224,16 +2224,20 @@
 xception
+ ex)
  %7B%0A     
@@ -2243,72 +2243,53 @@
     
-return conscryptClass.getMethod(%22isConscrypt%22, SSLEngine.class);
+    throw new RuntimeException(ex);%0A        %7D
 %0A   

Load Conscrypt method via reflection only once