diff --git a/driver/pom.xml b/driver/pom.xml
index e4b17991a2..98ceab6bb7 100644
--- a/driver/pom.xml
+++ b/driver/pom.xml
@@ -78,6 +78,10 @@
io.projectreactor
reactor-test
+
+ com.oracle.substratevm
+ svm
+
@@ -232,6 +236,14 @@
+
+
+ io.netty:*
+
+ META-INF/native-image/**
+
+
+
true
true
diff --git a/driver/src/main/java/org/neo4j/driver/internal/svm/NettySubstitutions.java b/driver/src/main/java/org/neo4j/driver/internal/svm/NettySubstitutions.java
new file mode 100644
index 0000000000..113b4aa304
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/internal/svm/NettySubstitutions.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2002-2019 "Neo4j,"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Licensed 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 org.neo4j.driver.internal.svm;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import com.oracle.svm.core.jdk.JDK11OrLater;
+
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.cert.X509Certificate;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingDeque;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+
+import io.netty.bootstrap.AbstractBootstrapConfig;
+import io.netty.bootstrap.ChannelFactory;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.DefaultChannelPromise;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.handler.ssl.CipherSuiteFilter;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator;
+import io.netty.handler.ssl.JdkApplicationProtocolNegotiator;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.JdkLoggerFactory;
+
+/**
+ * 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.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", onlyWith = JDK11OrLater.class)
+final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator_AlpnWrapper {
+ @Substitute
+ @SuppressWarnings( "deprecation" )
+ public SSLEngine wrapSslEngine( SSLEngine engine, ByteBufAllocator alloc,
+ JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) {
+ return (SSLEngine) (Object) new Target_io_netty_handler_ssl_Java9SslEngine(
+ engine, applicationNegotiator, isServer);
+ }
+
+}
+
+@TargetClass(className = "io.netty.handler.ssl.Java9SslEngine", onlyWith = JDK11OrLater.class)
+final class Target_io_netty_handler_ssl_Java9SslEngine {
+ @Alias
+ @SuppressWarnings( "deprecation" )
+ Target_io_netty_handler_ssl_Java9SslEngine(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)
+ 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) 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<>();
+ }
+}
+
+class NettySubstitutions {
+
+}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/svm/ZLibSubstitutions.java b/driver/src/main/java/org/neo4j/driver/internal/svm/ZLibSubstitutions.java
new file mode 100644
index 0000000000..45ff698453
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/internal/svm/ZLibSubstitutions.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2002-2019 "Neo4j,"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Licensed 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 org.neo4j.driver.internal.svm;
+
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import io.netty.handler.codec.compression.JdkZlibDecoder;
+import io.netty.handler.codec.compression.JdkZlibEncoder;
+import io.netty.handler.codec.compression.ZlibDecoder;
+import io.netty.handler.codec.compression.ZlibEncoder;
+import io.netty.handler.codec.compression.ZlibWrapper;
+
+/**
+ * This substitution avoid having jcraft zlib added to the build
+ */
+@TargetClass(className = "io.netty.handler.codec.compression.ZlibCodecFactory")
+final class Target_org_neo4j_driver_internal_shaded_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/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/native-image.properties b/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/native-image.properties
new file mode 100644
index 0000000000..646ac84bc9
--- /dev/null
+++ b/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/native-image.properties
@@ -0,0 +1,18 @@
+Args = -H:ReflectionConfigurationResources=${.}/reflection-config.json \
+ --initialize-at-run-time=org.neo4j.driver.internal.async.connection.BoltProtocolUtil \
+ --initialize-at-run-time=org.neo4j.driver.internal.async.connection.ChannelConnectedListener \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.ByteBufAllocator \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.ByteBufUtil \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.PooledByteBufAllocator \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.UnpooledHeapByteBuf \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.buffer.UnreleasableByteBuf \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.handler.ssl.Conscrypt \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.handler.ssl.ConscryptAlpnSslEngine \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.handler.ssl.JdkNpnApplicationProtocolNegotiator \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.handler.ssl.ReferenceCountedOpenSslEngine \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.handler.ssl.util.ThreadLocalInsecureRandom \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.util.AbstractReferenceCounted \
+ --initialize-at-run-time=org.neo4j.driver.internal.shaded.io.netty.util.internal.logging.Log4JLogger \
+ -Dio.netty.noUnsafe=true \
+ -Dio.netty.leakDetection.level=DISABLED
diff --git a/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/reflection-config.json b/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/reflection-config.json
new file mode 100644
index 0000000000..869951c69f
--- /dev/null
+++ b/driver/src/main/resources/META-INF/native-image/org.neo4j/driver/reflection-config.json
@@ -0,0 +1,22 @@
+[
+ {
+ "name": "org.neo4j.driver.internal.shaded.io.netty.channel.socket.nio.NioSocketChannel",
+ "methods": [
+ { "name": "", "parameterTypes": [] }
+ ]
+ },
+ {
+ "name": "org.neo4j.driver.internal.shaded.io.netty.channel.socket.nio.NioServerSocketChannel",
+ "methods": [
+ { "name": "", "parameterTypes": [] }
+ ]
+ },
+ {
+ "name" : "org.neo4j.driver.internal.shaded.io.netty.buffer.AbstractByteBufAllocator",
+ "allDeclaredMethods" : true
+ },
+ {
+ "name" : "org.neo4j.driver.internal.shaded.io.netty.util.ReferenceCountUtil",
+ "allDeclaredMethods" : true
+ }
+]
diff --git a/pom.xml b/pom.xml
index e253af1498..22a2a57e91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,6 +140,13 @@
3.2.6.RELEASE
test
+
+ com.oracle.substratevm
+ svm
+ 19.2.0.1
+
+ provided
+