diff --git a/pom.xml b/pom.xml
index fa2e6e3..00cba70 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,7 @@
com.example.ApplicationKt
1.9.23
7.7.0-SNAPSHOT
+ 24.0.1
@@ -316,7 +317,34 @@
arangodb-java-driver
${adb.version}
+
+ org.graalvm.sdk
+ graal-sdk
+ 24.0.1
+ provided
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+
shaded
diff --git a/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties
index c495ad1..fde6afe 100644
--- a/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties
+++ b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties
@@ -1,5 +1,43 @@
Args=\
---initialize-at-build-time=\
- com.example,\
- ch.qos.logback,\
- org.slf4j
+ -Dio.netty.noUnsafe=true \
+ -Dio.netty.leakDetection.level=DISABLED \
+ --initialize-at-build-time=\
+ com.example,\
+ ch.qos.logback,\
+ org.slf4j,\
+ io.netty \
+ --initialize-at-run-time=\
+ io.netty.buffer.PooledByteBufAllocator,\
+ io.netty.buffer.ByteBufAllocator,\
+ io.netty.buffer.ByteBufUtil,\
+ io.netty.buffer.AbstractReferenceCountedByteBuf,\
+ io.netty.handler.ssl.JdkSslServerContext,\
+ io.netty.handler.codec.compression.BrotliDecoder,\
+ io.netty.handler.codec.compression.ZstdConstants,\
+ io.netty.handler.codec.http2.Http2CodecUtil,\
+ io.netty.handler.codec.http2.Http2ClientUpgradeCodec,\
+ io.netty.handler.codec.http2.Http2ConnectionHandler,\
+ io.netty.handler.codec.http2.DefaultHttp2FrameWriter,\
+ io.netty.handler.codec.http.HttpObjectEncoder,\
+ io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,\
+ io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder,\
+ io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler,\
+ io.netty.handler.codec.http2.Http2ServerUpgradeCodec,\
+ io.netty.handler.pcap.PcapWriteHandler$WildcardAddressHolder,\
+ io.netty.util.AbstractReferenceCounted,\
+ io.netty.util.concurrent.GlobalEventExecutor,\
+ io.netty.util.concurrent.ImmediateEventExecutor,\
+ io.netty.util.concurrent.ScheduledFutureTask,\
+ io.netty.util.internal.ThreadLocalRandom,\
+ io.netty.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder,\
+ io.netty.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder,\
+ io.netty.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder,\
+ io.netty.util.NetUtilSubstitutions$NetUtilNetworkInterfacesLazyHolder,\
+ io.netty.handler.ssl.util.ThreadLocalInsecureRandom,\
+ io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider,\
+ io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder,\
+ io.netty.resolver.dns.DnsNameResolver,\
+ io.netty.resolver.HostsFileEntriesResolver,\
+ io.netty.resolver.dns.ResolvConf$ResolvConfLazy,\
+ io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider,\
+ io.vertx.core.buffer.impl.VertxByteBufAllocator
diff --git a/src/native/java/graal/BrotliSubstitutions.java b/src/native/java/graal/BrotliSubstitutions.java
new file mode 100644
index 0000000..ccd245c
--- /dev/null
+++ b/src/native/java/graal/BrotliSubstitutions.java
@@ -0,0 +1,20 @@
+package graal;
+
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+
+public class BrotliSubstitutions {
+
+ @TargetClass(className = "io.netty.handler.codec.compression.Brotli")
+ static final class Target_io_netty_handler_codec_compression_Brotli {
+ @Substitute
+ public static boolean isAvailable() {
+ return false;
+ }
+
+ @Substitute
+ public static void ensureAvailability() throws Throwable {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/native/java/graal/graal/JdkSubstitutions.java b/src/native/java/graal/graal/JdkSubstitutions.java
new file mode 100644
index 0000000..049d946
--- /dev/null
+++ b/src/native/java/graal/graal/JdkSubstitutions.java
@@ -0,0 +1,98 @@
+package graal.graal;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.InjectAccessors;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import org.graalvm.nativeimage.Platform;
+import org.graalvm.nativeimage.Platforms;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+
+@TargetClass(className = "jdk.internal.loader.URLClassPath$Loader")
+final class Target_URLClassPath$Loader {
+
+ @Alias
+ public Target_URLClassPath$Loader(URL url) {
+ }
+}
+
+@TargetClass(className = "jdk.internal.loader.URLClassPath$FileLoader")
+final class Target_URLClassPath$FileLoader {
+
+ @Alias
+ public Target_URLClassPath$FileLoader(URL url) throws IOException {
+ }
+}
+
+@TargetClass(className = "jdk.internal.loader.URLClassPath")
+final class Target_jdk_internal_loader_URLClassPath {
+
+ @Substitute
+ private Target_URLClassPath$Loader getLoader(final URL url) throws IOException {
+ String file = url.getFile();
+ if (file != null && file.endsWith("/")) {
+ if ("file".equals(url.getProtocol())) {
+ return (Target_URLClassPath$Loader) (Object) new Target_URLClassPath$FileLoader(
+ url);
+ } else {
+ return new Target_URLClassPath$Loader(url);
+ }
+ } else {
+ // that must be wrong, but JarLoader is deleted by SVM
+ return (Target_URLClassPath$Loader) (Object) new Target_URLClassPath$FileLoader(
+ url);
+ }
+ }
+
+}
+
+@Substitute
+@TargetClass(className = "sun.nio.ch.WindowsAsynchronousFileChannelImpl", innerClass = "DefaultIocpHolder")
+@Platforms({ Platform.WINDOWS.class })
+final class Target_sun_nio_ch_WindowsAsynchronousFileChannelImpl_DefaultIocpHolder {
+
+ @Alias
+ @InjectAccessors(DefaultIocpAccessor.class)
+ static Target_sun_nio_ch_Iocp defaultIocp;
+}
+
+@TargetClass(className = "sun.nio.ch.Iocp")
+@Platforms({ Platform.WINDOWS.class })
+final class Target_sun_nio_ch_Iocp {
+
+ @Alias
+ Target_sun_nio_ch_Iocp(AsynchronousChannelProvider provider, Target_sun_nio_ch_ThreadPool pool) throws IOException {
+ }
+
+ @Alias
+ Target_sun_nio_ch_Iocp start() {
+ return null;
+ }
+}
+
+@TargetClass(className = "sun.nio.ch.ThreadPool")
+@Platforms({ Platform.WINDOWS.class })
+final class Target_sun_nio_ch_ThreadPool {
+
+ @Alias
+ static Target_sun_nio_ch_ThreadPool createDefault() {
+ return null;
+ }
+}
+
+final class DefaultIocpAccessor {
+ static Target_sun_nio_ch_Iocp get() {
+ try {
+ return new Target_sun_nio_ch_Iocp(null, Target_sun_nio_ch_ThreadPool.createDefault()).start();
+ } catch (IOException ioe) {
+ throw new InternalError(ioe);
+ }
+ }
+}
+
+class JdkSubstitutions {
+
+}
diff --git a/src/native/java/graal/graal/VertxSubstitutions.java b/src/native/java/graal/graal/VertxSubstitutions.java
new file mode 100644
index 0000000..6419ccc
--- /dev/null
+++ b/src/native/java/graal/graal/VertxSubstitutions.java
@@ -0,0 +1,197 @@
+package graal.graal;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import io.netty.handler.ssl.*;
+import io.vertx.core.MultiMap;
+import io.vertx.core.Promise;
+import io.vertx.core.Vertx;
+import io.vertx.core.dns.AddressResolverOptions;
+import io.vertx.core.eventbus.EventBusOptions;
+import io.vertx.core.eventbus.impl.HandlerHolder;
+import io.vertx.core.eventbus.impl.HandlerRegistration;
+import io.vertx.core.eventbus.impl.MessageImpl;
+import io.vertx.core.eventbus.impl.OutboundDeliveryContext;
+import io.vertx.core.impl.ContextInternal;
+import io.vertx.core.impl.VertxInternal;
+import io.vertx.core.impl.resolver.DefaultResolverProvider;
+import io.vertx.core.impl.transports.JDKTransport;
+import io.vertx.core.net.NetServerOptions;
+import io.vertx.core.spi.resolver.ResolverProvider;
+import io.vertx.core.spi.transport.Transport;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+@TargetClass(className = "io.vertx.core.impl.VertxBuilder")
+final class Target_io_vertx_core_impl_VertxBuilder {
+ @Substitute
+ public static Transport nativeTransport() {
+ return JDKTransport.INSTANCE;
+ }
+}
+
+/**
+ * This substitution forces the usage of the blocking DNS resolver
+ */
+@TargetClass(className = "io.vertx.core.spi.resolver.ResolverProvider")
+final class TargetResolverProvider {
+
+ @Substitute
+ public static ResolverProvider factory(Vertx vertx, AddressResolverOptions options) {
+ return new DefaultResolverProvider();
+ }
+}
+
+@TargetClass(className = "io.vertx.core.net.OpenSSLEngineOptions")
+final class Target_io_vertx_core_net_OpenSSLEngineOptions {
+
+ @Substitute
+ public static boolean isAvailable() {
+ return false;
+ }
+
+ @Substitute
+ public static boolean isAlpnAvailable() {
+ return false;
+ }
+}
+
+@SuppressWarnings("rawtypes")
+@TargetClass(className = "io.vertx.core.eventbus.impl.clustered.ClusteredEventBus")
+final class Target_io_vertx_core_eventbus_impl_clustered_ClusteredEventBusClusteredEventBus {
+
+ @Substitute
+ private NetServerOptions getServerOptions() {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ public void start(Promise promise) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ public void close(Promise promise) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ public MessageImpl createMessage(boolean send, boolean isLocal, String address, MultiMap headers, Object body,
+ String codecName) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected void onLocalRegistration(HandlerHolder handlerHolder, Promise promise) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected HandlerHolder createHandlerHolder(HandlerRegistration registration, boolean replyHandler,
+ boolean localOnly, ContextInternal context) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected void onLocalUnregistration(HandlerHolder handlerHolder, Promise completionHandler) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected void sendOrPub(OutboundDeliveryContext sendContext) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected String generateReplyAddress() {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ protected boolean isMessageLocal(MessageImpl msg) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ ConcurrentMap connections() {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ VertxInternal vertx() {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Substitute
+ EventBusOptions options() {
+ throw new RuntimeException("Not Implemented");
+ }
+}
+
+@TargetClass(className = "io.vertx.core.spi.tls.DefaultSslContextFactory")
+final class Target_DefaultSslContextFactory {
+
+ @Alias
+ private Set enabledCipherSuites;
+
+ @Alias
+ private List applicationProtocols;
+
+ @Alias
+ private ClientAuth clientAuth;
+
+ @Substitute
+ private SslContext createContext(boolean useAlpn, boolean client, KeyManagerFactory kmf, TrustManagerFactory tmf)
+ throws SSLException {
+ SslContextBuilder builder;
+ if (client) {
+ builder = SslContextBuilder.forClient();
+ if (kmf != null) {
+ builder.keyManager(kmf);
+ }
+ } else {
+ builder = SslContextBuilder.forServer(kmf);
+ }
+ Collection cipherSuites = enabledCipherSuites;
+ builder.sslProvider(SslProvider.JDK);
+ if (cipherSuites == null || cipherSuites.isEmpty()) {
+ cipherSuites = Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite.get();
+ }
+ if (tmf != null) {
+ builder.trustManager(tmf);
+ }
+ if (cipherSuites != null && cipherSuites.size() > 0) {
+ builder.ciphers(cipherSuites);
+ }
+ if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) {
+ builder.applicationProtocolConfig(new ApplicationProtocolConfig(
+ ApplicationProtocolConfig.Protocol.ALPN,
+ ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
+ ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
+ applicationProtocols));
+ }
+ if (clientAuth != null) {
+ builder.clientAuth(clientAuth);
+ }
+ return builder.build();
+ }
+}
+
+@TargetClass(className = "io.vertx.core.spi.tls.DefaultJDKCipherSuite")
+final class Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite {
+ @Alias
+ static List get() {
+ return null;
+ }
+}
+
+class VertxSubstitutions {
+
+}
diff --git a/src/native/java/graal/graal/package-info.java b/src/native/java/graal/graal/package-info.java
new file mode 100644
index 0000000..794dbcf
--- /dev/null
+++ b/src/native/java/graal/graal/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * from io.quarkus:quarkus-vertx:3.10.1
+ */
+package graal.graal;
diff --git a/src/native/java/graal/netty/EmptyByteBufStub.java b/src/native/java/graal/netty/EmptyByteBufStub.java
new file mode 100644
index 0000000..1dc6dab
--- /dev/null
+++ b/src/native/java/graal/netty/EmptyByteBufStub.java
@@ -0,0 +1,33 @@
+package graal.netty;
+
+import io.netty.util.internal.PlatformDependent;
+
+import java.nio.ByteBuffer;
+
+public final class EmptyByteBufStub {
+ private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocateDirect(0);
+ private static final long EMPTY_BYTE_BUFFER_ADDRESS;
+
+ static {
+ long emptyByteBufferAddress = 0;
+ try {
+ if (PlatformDependent.hasUnsafe()) {
+ emptyByteBufferAddress = PlatformDependent.directBufferAddress(EMPTY_BYTE_BUFFER);
+ }
+ } catch (Throwable t) {
+ // Ignore
+ }
+ EMPTY_BYTE_BUFFER_ADDRESS = emptyByteBufferAddress;
+ }
+
+ public static ByteBuffer emptyByteBuffer() {
+ return EMPTY_BYTE_BUFFER;
+ }
+
+ public static long emptyByteBufferAddress() {
+ return EMPTY_BYTE_BUFFER_ADDRESS;
+ }
+
+ private EmptyByteBufStub() {
+ }
+}
diff --git a/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java b/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java
new file mode 100644
index 0000000..2489e97
--- /dev/null
+++ b/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java
@@ -0,0 +1,68 @@
+package graal.netty.graal;
+
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+
+import java.util.function.BooleanSupplier;
+
+public class HttpContentCompressorSubstitutions {
+
+ @TargetClass(className = "io.netty.handler.codec.compression.ZstdEncoder", onlyWith = IsZstdAbsent.class)
+ public static final class ZstdEncoderFactorySubstitution {
+
+ @Substitute
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Substitute
+ protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Substitute
+ public void flush(final ChannelHandlerContext ctx) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Substitute
+ @TargetClass(className = "io.netty.handler.codec.compression.ZstdConstants", onlyWith = IsZstdAbsent.class)
+ public static final class ZstdConstants {
+
+ // The constants make calls to com.github.luben.zstd.Zstd so we cut links with that substitution.
+
+ static final int DEFAULT_COMPRESSION_LEVEL = 0;
+
+ static final int MIN_COMPRESSION_LEVEL = 0;
+
+ static final int MAX_COMPRESSION_LEVEL = 0;
+
+ static final int MAX_BLOCK_SIZE = 0;
+
+ static final int DEFAULT_BLOCK_SIZE = 0;
+ }
+
+ public static class IsZstdAbsent implements BooleanSupplier {
+
+ private boolean zstdAbsent;
+
+ public IsZstdAbsent() {
+ try {
+ Class.forName("com.github.luben.zstd.Zstd");
+ zstdAbsent = false;
+ } catch (Exception e) {
+ // It can be a classloading issue (the library is not available), or a native issue
+ // (the library for the current OS/arch is not available)
+ zstdAbsent = true;
+ }
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return zstdAbsent;
+ }
+ }
+}
diff --git a/src/native/java/graal/netty/graal/NettySubstitutions.java b/src/native/java/graal/netty/graal/NettySubstitutions.java
new file mode 100644
index 0000000..fb9b8b1
--- /dev/null
+++ b/src/native/java/graal/netty/graal/NettySubstitutions.java
@@ -0,0 +1,606 @@
+package graal.netty.graal;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.RecomputeFieldValue;
+import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import graal.netty.EmptyByteBufStub;
+import io.netty.bootstrap.AbstractBootstrapConfig;
+import io.netty.bootstrap.ChannelFactory;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.*;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.compression.ZlibCodecFactory;
+import io.netty.handler.codec.compression.ZlibWrapper;
+import io.netty.handler.codec.http.HttpHeaderValues;
+import io.netty.handler.codec.http2.Http2Exception;
+import io.netty.handler.ssl.*;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.JdkLoggerFactory;
+
+import javax.crypto.NoSuchPaddingException;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.function.BooleanSupplier;
+
+import static io.netty.handler.codec.http.HttpHeaderValues.*;
+
+/**
+ * This substitution avoid having loggers added to the build
+ */
+@TargetClass(className = "io.netty.util.internal.logging.InternalLoggerFactory")
+final class Target_io_netty_util_internal_logging_InternalLoggerFactory {
+
+ @Substitute
+ private static InternalLoggerFactory newDefaultFactory(String name) {
+ return JdkLoggerFactory.INSTANCE;
+ }
+}
+
+// SSL
+// This whole section is mostly about removing static analysis references to openssl/tcnative
+
+@TargetClass(className = "io.netty.handler.ssl.SslProvider")
+final class Target_io_netty_handler_ssl_SslProvider {
+ @Substitute
+ public static boolean isAlpnSupported(final SslProvider provider) {
+ switch (provider) {
+ case JDK:
+ return Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator.isAlpnSupported();
+ case OPENSSL:
+ case OPENSSL_REFCNT:
+ return false;
+ default:
+ throw new Error("SslProvider unsupported on Quarkus " + provider);
+ }
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator")
+final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator {
+ @Alias
+ static boolean isAlpnSupported() {
+ return true;
+ }
+}
+
+/**
+ * Hardcode io.netty.handler.ssl.OpenSsl as non-available
+ */
+@TargetClass(className = "io.netty.handler.ssl.OpenSsl")
+final class Target_io_netty_handler_ssl_OpenSsl {
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ private static Throwable UNAVAILABILITY_CAUSE = new RuntimeException("OpenSsl unsupported on Quarkus");
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ static List DEFAULT_CIPHERS = Collections.emptyList();
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ static Set AVAILABLE_CIPHER_SUITES = Collections.emptySet();
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ private static Set AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ private static Set AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ private static boolean SUPPORTS_KEYMANAGER_FACTORY = false;
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ private static boolean SUPPORTS_OCSP = false;
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FromAlias)
+ static Set SUPPORTED_PROTOCOLS_SET = Collections.emptySet();
+
+ @Substitute
+ public static boolean isAvailable() {
+ return false;
+ }
+
+ @Substitute
+ public static int version() {
+ return -1;
+ }
+
+ @Substitute
+ public static String versionString() {
+ return null;
+ }
+
+ @Substitute
+ public static boolean isCipherSuiteAvailable(String cipherSuite) {
+ return false;
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkSslServerContext")
+final class Target_io_netty_handler_ssl_JdkSslServerContext {
+
+ @Alias
+ Target_io_netty_handler_ssl_JdkSslServerContext(Provider provider,
+ X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
+ X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
+ KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter,
+ ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
+ ClientAuth clientAuth, String[] protocols, boolean startTls,
+ String keyStore)
+ throws SSLException {
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkSslClientContext")
+final class Target_io_netty_handler_ssl_JdkSslClientContext {
+
+ @Alias
+ Target_io_netty_handler_ssl_JdkSslClientContext(Provider sslContextProvider, X509Certificate[] trustCertCollection,
+ TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key,
+ String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers,
+ CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
+ long sessionCacheSize, long sessionTimeout, String keyStoreType)
+ throws SSLException {
+
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.SslHandler$SslEngineType")
+final class Target_io_netty_handler_ssl_SslHandler$SslEngineType {
+
+ @Alias
+ public static Target_io_netty_handler_ssl_SslHandler$SslEngineType JDK;
+
+ @Substitute
+ static Target_io_netty_handler_ssl_SslHandler$SslEngineType forEngine(SSLEngine engine) {
+ return JDK;
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator$AlpnWrapper")
+final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator_AlpnWrapper {
+ @Substitute
+ public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc,
+ JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) {
+ return (SSLEngine) (Object) new Target_io_netty_handler_ssl_JdkAlpnSslEngine(engine, applicationNegotiator,
+ isServer);
+ }
+
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkAlpnSslEngine")
+final class Target_io_netty_handler_ssl_JdkAlpnSslEngine {
+ @Alias
+ Target_io_netty_handler_ssl_JdkAlpnSslEngine(final SSLEngine engine,
+ final JdkApplicationProtocolNegotiator applicationNegotiator, final boolean isServer) {
+
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.SslContext")
+final class Target_io_netty_handler_ssl_SslContext {
+
+ @Substitute
+ static SslContext newServerContextInternal(SslProvider provider, Provider sslContextProvider,
+ X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
+ X509Certificate[] keyCertChain,
+ PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers,
+ CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
+ ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, String keyStoreType,
+ Map.Entry, Object>... ctxOptions) throws SSLException {
+ if (enableOcsp) {
+ throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
+ }
+ return (SslContext) (Object) new Target_io_netty_handler_ssl_JdkSslServerContext(sslContextProvider,
+ trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
+ keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
+ clientAuth, protocols, startTls, keyStoreType);
+ }
+
+ @Substitute
+ static SslContext newClientContextInternal(SslProvider provider, Provider sslContextProvider,
+ X509Certificate[] trustCert,
+ TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
+ KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter,
+ ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout,
+ boolean enableOcsp,
+ String keyStoreType, Map.Entry, Object>... options) throws SSLException {
+ if (enableOcsp) {
+ throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
+ }
+ return (SslContext) (Object) new Target_io_netty_handler_ssl_JdkSslClientContext(sslContextProvider,
+ trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
+ keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize,
+ sessionTimeout, keyStoreType);
+ }
+
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkDefaultApplicationProtocolNegotiator")
+final class Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator {
+
+ @Alias
+ public static Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator INSTANCE;
+}
+
+@TargetClass(className = "io.netty.handler.ssl.JdkSslContext")
+final class Target_io_netty_handler_ssl_JdkSslContext {
+
+ @Substitute
+ static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
+ if (config == null) {
+ return (JdkApplicationProtocolNegotiator) (Object) Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator.INSTANCE;
+ }
+
+ switch (config.protocol()) {
+ case NONE:
+ return (JdkApplicationProtocolNegotiator) (Object) Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator.INSTANCE;
+ case ALPN:
+ if (isServer) {
+ // GRAAL RC9 bug: https://github.com/oracle/graal/issues/813
+ // switch(config.selectorFailureBehavior()) {
+ // case FATAL_ALERT:
+ // return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
+ // case NO_ADVERTISE:
+ // return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
+ // default:
+ // throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
+ // .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
+ // }
+ SelectorFailureBehavior behavior = config.selectorFailureBehavior();
+ if (behavior == SelectorFailureBehavior.FATAL_ALERT) {
+ return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
+ } else if (behavior == SelectorFailureBehavior.NO_ADVERTISE) {
+ return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
+ } else {
+ throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
+ .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
+ }
+ } else {
+ switch (config.selectedListenerFailureBehavior()) {
+ case ACCEPT:
+ return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
+ case FATAL_ALERT:
+ return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
+ default:
+ throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
+ .append(config.selectedListenerFailureBehavior()).append(" failure behavior")
+ .toString());
+ }
+ }
+ default:
+ throw new UnsupportedOperationException(
+ new StringBuilder("JDK provider does not support ").append(config.protocol())
+ .append(" protocol")
+ .toString());
+ }
+ }
+
+}
+
+/*
+ * This one only prints exceptions otherwise we get a useless bogus
+ * exception message: https://github.com/eclipse-vertx/vert.x/issues/1657
+ */
+@TargetClass(className = "io.netty.bootstrap.AbstractBootstrap")
+final class Target_io_netty_bootstrap_AbstractBootstrap {
+
+ @Alias
+ private ChannelFactory channelFactory;
+
+ @Alias
+ void init(Channel channel) throws Exception {
+ }
+
+ @Alias
+ public AbstractBootstrapConfig config() {
+ return null;
+ }
+
+ @Substitute
+ final ChannelFuture initAndRegister() {
+ Channel channel = null;
+ try {
+ channel = channelFactory.newChannel();
+ init(channel);
+ } catch (Throwable t) {
+ // THE FIX IS HERE:
+ t.printStackTrace();
+ if (channel != null) {
+ // channel can be null if newChannel crashed (eg SocketException("too many open files"))
+ channel.unsafe().closeForcibly();
+ }
+ // as the Channel is not registered yet, we need to force the usage of the GlobalEventExecutor
+ return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
+ }
+
+ ChannelFuture regFuture = config().group().register(channel);
+ if (regFuture.cause() != null) {
+ if (channel.isRegistered()) {
+ channel.close();
+ } else {
+ channel.unsafe().closeForcibly();
+ }
+ }
+
+ // If we are here and the promise is not failed, it's one of the following cases:
+ // 1) If we attempted registration from the event loop, the registration has been completed at this point.
+ // i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
+ // 2) If we attempted registration from the other thread, the registration request has been successfully
+ // added to the event loop's task queue for later execution.
+ // i.e. It's safe to attempt bind() or connect() now:
+ // because bind() or connect() will be executed *after* the scheduled registration task is executed
+ // because register(), bind(), and connect() are all bound to the same thread.
+
+ return regFuture;
+
+ }
+}
+
+@TargetClass(className = "io.netty.channel.nio.NioEventLoop")
+final class Target_io_netty_channel_nio_NioEventLoop {
+
+ @Substitute
+ private static Queue newTaskQueue0(int maxPendingTasks) {
+ return new LinkedBlockingDeque<>();
+ }
+}
+
+@TargetClass(className = "io.netty.buffer.AbstractReferenceCountedByteBuf")
+final class Target_io_netty_buffer_AbstractReferenceCountedByteBuf {
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FieldOffset, name = "refCnt")
+ private static long REFCNT_FIELD_OFFSET;
+}
+
+@TargetClass(className = "io.netty.util.AbstractReferenceCounted")
+final class Target_io_netty_util_AbstractReferenceCounted {
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.FieldOffset, name = "refCnt")
+ private static long REFCNT_FIELD_OFFSET;
+}
+
+// This class is runtime-initialized by NettyProcessor
+final class Holder_io_netty_util_concurrent_ScheduledFutureTask {
+ static final long START_TIME = System.nanoTime();
+}
+
+@TargetClass(className = "io.netty.util.concurrent.AbstractScheduledEventExecutor")
+final class Target_io_netty_util_concurrent_AbstractScheduledEventExecutor {
+
+ // The START_TIME field is kept but not used.
+ // All the accesses to it have been replaced with Holder_io_netty_util_concurrent_ScheduledFutureTask
+
+ @Substitute
+ static long initialNanoTime() {
+ return Holder_io_netty_util_concurrent_ScheduledFutureTask.START_TIME;
+ }
+
+ @Substitute
+ static long defaultCurrentTimeNanos() {
+ return System.nanoTime() - Holder_io_netty_util_concurrent_ScheduledFutureTask.START_TIME;
+ }
+}
+
+@TargetClass(className = "io.netty.channel.ChannelHandlerMask")
+final class Target_io_netty_channel_ChannelHandlerMask {
+
+ // Netty tries to self-optimized itself, but it requires lots of reflection. We disable this behavior and avoid
+ // misleading DEBUG messages in the log.
+ @Substitute
+ private static boolean isSkippable(final Class> handlerType, final String methodName, final Class... paramTypes) {
+ return false;
+ }
+}
+
+@TargetClass(className = "io.netty.util.internal.NativeLibraryLoader")
+final class Target_io_netty_util_internal_NativeLibraryLoader {
+
+ // This method can trick GraalVM into thinking that Classloader#defineClass is getting called
+ @Substitute
+ static Class> tryToLoadClass(final ClassLoader loader, final Class> helper)
+ throws ClassNotFoundException {
+ return Class.forName(helper.getName(), false, loader);
+ }
+
+}
+
+@TargetClass(className = "io.netty.buffer.EmptyByteBuf")
+final class Target_io_netty_buffer_EmptyByteBuf {
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.Reset)
+ private static ByteBuffer EMPTY_BYTE_BUFFER;
+
+ @Alias
+ @RecomputeFieldValue(kind = Kind.Reset)
+ private static long EMPTY_BYTE_BUFFER_ADDRESS;
+
+ @Substitute
+ public ByteBuffer nioBuffer() {
+ return EmptyByteBufStub.emptyByteBuffer();
+ }
+
+ @Substitute
+ public ByteBuffer[] nioBuffers() {
+ return new ByteBuffer[] { EmptyByteBufStub.emptyByteBuffer() };
+ }
+
+ @Substitute
+ public ByteBuffer internalNioBuffer(int index, int length) {
+ return EmptyByteBufStub.emptyByteBuffer();
+ }
+
+ @Substitute
+ public boolean hasMemoryAddress() {
+ return EmptyByteBufStub.emptyByteBufferAddress() != 0;
+ }
+
+ @Substitute
+ public long memoryAddress() {
+ if (hasMemoryAddress()) {
+ return EmptyByteBufStub.emptyByteBufferAddress();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
+
+@TargetClass(className = "io.netty.handler.codec.http.HttpContentDecompressor")
+final class Target_io_netty_handler_codec_http_HttpContentDecompressor {
+
+ @Alias
+ private boolean strict;
+
+ @Alias
+ protected ChannelHandlerContext ctx;
+
+ @Substitute
+ protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
+ if (GZIP.contentEqualsIgnoreCase(contentEncoding) ||
+ X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
+ }
+ if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) ||
+ X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
+ final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
+ // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
+ ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper));
+ }
+
+ // 'identity' or unsupported
+ return null;
+ }
+}
+
+@TargetClass(className = "io.netty.handler.codec.http2.DelegatingDecompressorFrameListener")
+final class Target_io_netty_handler_codec_http2_DelegatingDecompressorFrameListener {
+
+ @Alias
+ boolean strict;
+
+ @Substitute
+ protected EmbeddedChannel newContentDecompressor(ChannelHandlerContext ctx, CharSequence contentEncoding)
+ throws Http2Exception {
+ if (!HttpHeaderValues.GZIP.contentEqualsIgnoreCase(contentEncoding)
+ && !HttpHeaderValues.X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
+ if (!HttpHeaderValues.DEFLATE.contentEqualsIgnoreCase(contentEncoding)
+ && !HttpHeaderValues.X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
+ return null;
+ } else {
+ ZlibWrapper wrapper = this.strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(),
+ new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(wrapper) });
+ }
+ } else {
+ return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(),
+ new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP) });
+ }
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.SslHandler")
+final class Target_SslHandler {
+
+ @Substitute
+ private void setOpensslEngineSocketFd(Channel c) {
+ // do nothing.
+ }
+}
+
+@TargetClass(className = "io.netty.handler.ssl.PemReader")
+final class Alias_PemReader {
+
+ @Alias
+ public static ByteBuf readPrivateKey(File keyFile) {
+ return null;
+ }
+
+ @Alias
+ public static ByteBuf readPrivateKey(InputStream in) throws KeyException {
+ return null;
+ }
+}
+
+/**
+ * If BouncyCastle is not on the classpath, we must not try to read the PEM file using the BouncyCatle PEM reader.
+ */
+@TargetClass(className = "io.netty.handler.ssl.SslContext", onlyWith = IsBouncyNotThere.class)
+final class Target_SslContext {
+
+ @Substitute
+ protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException,
+ NoSuchPaddingException, InvalidKeySpecException,
+ InvalidAlgorithmParameterException,
+ KeyException, IOException {
+ if (keyFile == null) {
+ return null;
+ }
+
+ return getPrivateKeyFromByteBuffer(Alias_PemReader.readPrivateKey(keyFile), keyPassword);
+ }
+
+ @Substitute
+ protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword)
+ throws NoSuchAlgorithmException,
+ NoSuchPaddingException, InvalidKeySpecException,
+ InvalidAlgorithmParameterException,
+ KeyException, IOException {
+ if (keyInputStream == null) {
+ return null;
+ }
+
+ return getPrivateKeyFromByteBuffer(Alias_PemReader.readPrivateKey(keyInputStream), keyPassword);
+ }
+
+ @Alias
+ private static PrivateKey getPrivateKeyFromByteBuffer(ByteBuf encodedKeyBuf, String keyPassword)
+ throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
+ InvalidAlgorithmParameterException, KeyException, IOException {
+ return null;
+ }
+}
+
+class IsBouncyNotThere implements BooleanSupplier {
+
+ @Override
+ public boolean getAsBoolean() {
+ try {
+ NettySubstitutions.class.getClassLoader().loadClass("org.bouncycastle.openssl.PEMParser");
+ return false;
+ } catch (Exception e) {
+ return true;
+ }
+ }
+}
+
+class NettySubstitutions {
+
+}
diff --git a/src/native/java/graal/netty/graal/ZLibSubstitutions.java b/src/native/java/graal/netty/graal/ZLibSubstitutions.java
new file mode 100644
index 0000000..7017aaa
--- /dev/null
+++ b/src/native/java/graal/netty/graal/ZLibSubstitutions.java
@@ -0,0 +1,66 @@
+package graal.netty.graal;
+
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import io.netty.handler.codec.compression.*;
+
+/**
+ * This substitution avoid having jcraft zlib added to the build
+ */
+@TargetClass(className = "io.netty.handler.codec.compression.ZlibCodecFactory")
+final class Target_io_netty_handler_codec_compression_ZlibCodecFactory {
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(int compressionLevel) {
+ return new JdkZlibEncoder(compressionLevel);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper) {
+ return new JdkZlibEncoder(wrapper);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel) {
+ return new JdkZlibEncoder(wrapper, compressionLevel);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) {
+ return new JdkZlibEncoder(wrapper, compressionLevel);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(byte[] dictionary) {
+ return new JdkZlibEncoder(dictionary);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(int compressionLevel, byte[] dictionary) {
+ return new JdkZlibEncoder(compressionLevel, dictionary);
+ }
+
+ @Substitute
+ public static ZlibEncoder newZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) {
+ return new JdkZlibEncoder(compressionLevel, dictionary);
+ }
+
+ @Substitute
+ public static ZlibDecoder newZlibDecoder() {
+ return new JdkZlibDecoder();
+ }
+
+ @Substitute
+ public static ZlibDecoder newZlibDecoder(ZlibWrapper wrapper) {
+ return new JdkZlibDecoder(wrapper);
+ }
+
+ @Substitute
+ public static ZlibDecoder newZlibDecoder(byte[] dictionary) {
+ return new JdkZlibDecoder(dictionary);
+ }
+}
+
+class ZLibSubstitutions {
+
+}
diff --git a/src/native/java/graal/netty/package-info.java b/src/native/java/graal/netty/package-info.java
new file mode 100644
index 0000000..8b55354
--- /dev/null
+++ b/src/native/java/graal/netty/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * from io.quarkus:quarkus-netty:3.10.1
+ */
+package graal.netty;