package org.glassfish.grizzly.http2;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.logging.Logger;
import javax.net.ssl.SSLEngine;
class AplnExtensionCompatibility {
private static final Logger LOG = Logger.getLogger(AplnExtensionCompatibility.class.getName());
private static final String IMPL_CLASS_NAME = "sun.security.ssl.SSLEngineImpl";
private static final String METHOD_NAME = "setHandshakeApplicationProtocolSelector";
private static AplnExtensionCompatibility INSTANCE;
private final boolean alpnExtensionGrizzly;
private final boolean protocolSelectorSetterInApi;
private final boolean protocolSelectorSetterInImpl;
public static synchronized AplnExtensionCompatibility getInstance() {
if (INSTANCE == null) {
INSTANCE = new AplnExtensionCompatibility();
}
return INSTANCE;
}
public boolean isAlpnExtensionAvailable() {
return isAlpnExtensionGrizzly() || isProtocolSelectorSetterInApi() || isProtocolSelectorSetterInImpl();
}
public boolean isAlpnExtensionGrizzly() {
return alpnExtensionGrizzly;
}
public boolean isProtocolSelectorSetterInApi() {
return protocolSelectorSetterInApi;
}
public boolean isProtocolSelectorSetterInImpl() {
return protocolSelectorSetterInImpl;
}
public Method getProtocolSelectorSetter(final SSLEngine engine) {
Objects.requireNonNull(engine, "engine");
try {
final Class<? extends SSLEngine> engineClass;
if (isHandshakeSetterInApi()) {
engineClass = SSLEngine.class;
} else {
engineClass = engine.getClass();
}
return engineClass.getMethod(METHOD_NAME, BiFunction.class);
} catch (final NoSuchMethodException e) {
throw new IllegalArgumentException("The method public void setHandshakeApplicationProtocolSelector("
+ "BiFunction<SSLEngine, List<String>, String> selector) is not declared by"
+ " the " + engine.getClass().getName() + ".", e);
}
}
private AplnExtensionCompatibility() {
this.alpnExtensionGrizzly = isClassAvailableOnBootstrapClasspath("sun.security.ssl.GrizzlyNPN");
this.protocolSelectorSetterInApi = isHandshakeSetterInApi();
this.protocolSelectorSetterInImpl = isHandshakeSetterInImpl();
}
private static boolean isClassAvailableOnBootstrapClasspath(final String className) {
try {
ClassLoader.getSystemClassLoader().loadClass(className);
return true;
} catch (final ClassNotFoundException e) {
LOG.config("The class with the name '" + className + "' is not available on the bootstrap classpath.");
return false;
}
}
private static boolean isHandshakeSetterInImpl() {
try {
Class.forName(IMPL_CLASS_NAME).getMethod(METHOD_NAME, BiFunction.class);
return true;
} catch (final IllegalAccessError e) {
LOG.config(() -> "The class " + IMPL_CLASS_NAME + " is not accessible.");
return false;
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
LOG.config(() -> "The class " + IMPL_CLASS_NAME + " cloud not be found.");
return false;
} catch (final NoSuchMethodException e) {
LOG.config(() -> "The method public void setHandshakeApplicationProtocolSelector("
+ "BiFunction<SSLEngine, List<String>, String> selector) is not declared by"
+ " the " + IMPL_CLASS_NAME + " class.");
return false;
}
}
private static boolean isHandshakeSetterInApi() {
try {
SSLEngine.class.getMethod(METHOD_NAME, BiFunction.class);
return true;
} catch (final NoSuchMethodException e) {
LOG.config("The method public void setHandshakeApplicationProtocolSelector("
+ "BiFunction<SSLEngine, List<String>, String> selector) is not declared by"
+ " the " + SSLEngine.class.getName() + ".");
return false;
}
}
@Override
public String toString() {
return super.toString() + "ALPN available: " + isAlpnExtensionAvailable()
+ ", ALPN is Grizzly: " + isAlpnExtensionGrizzly()
+ ", setHandshakeApplicationProtocolSelector in API: " + isProtocolSelectorSetterInApi()
+ ", setHandshakeApplicationProtocolSelector in impl: " + isProtocolSelectorSetterInImpl();
}
}