diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml index f7532d404..acca6dc1e 100644 --- a/.github/workflows/maven-deploy.yml +++ b/.github/workflows/maven-deploy.yml @@ -18,7 +18,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '8' + java-version: '11' distribution: 'adopt' cache: 'maven' server-id: ossrh diff --git a/.github/workflows/maven-release.yml b/.github/workflows/maven-release.yml index 61217e1b3..f8604f238 100644 --- a/.github/workflows/maven-release.yml +++ b/.github/workflows/maven-release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '8' + java-version: '11' distribution: 'adopt' cache: 'maven' server-id: ossrh diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index 9df54ccf5..c882a7864 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -16,12 +16,12 @@ jobs: docker-img: - docker.io/arangodb/enterprise:3.10.1 topology: - - cluster + - single java-version: - 17 module: - driver - - shaded-integration-tests + - integration-tests steps: - uses: actions/checkout@v2 @@ -60,7 +60,7 @@ jobs: - 17 module: - driver - - shaded-integration-tests + - integration-tests steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d4bef6589..05144f993 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,7 @@ jobs: db-ext-names: - false java-version: - - 8 + - 11 user-language: - en include: @@ -222,7 +222,7 @@ jobs: - name: Test run: mvn --no-transfer-progress -am -pl driver test -Dadb.jackson.version=${{matrix.jackson-version}} - shaded-integration-tests: + integration-tests: timeout-minutes: 20 runs-on: ubuntu-latest @@ -255,14 +255,17 @@ jobs: - name: Install run: mvn --no-transfer-progress install -DskipTests=true -Dgpg.skip=true -Dmaven.javadoc.skip=true - name: Test internal-serde - working-directory: shaded-integration-tests + working-directory: integration-tests run: mvn --no-transfer-progress -Pinternal-serde test - name: Test jackson-serde - working-directory: shaded-integration-tests + working-directory: integration-tests run: mvn --no-transfer-progress -Pjackson-serde test - name: Test jsonb-serde - working-directory: shaded-integration-tests + working-directory: integration-tests run: mvn --no-transfer-progress -Pjsonb-serde test + - name: Test plain + working-directory: integration-tests + run: mvn --no-transfer-progress -Pplain test sonar: timeout-minutes: 10 diff --git a/core/src/main/java/com/arangodb/async/ArangoDBAsync.java b/core/src/main/java/com/arangodb/async/ArangoDBAsync.java index 98386c500..7d9ce542d 100644 --- a/core/src/main/java/com/arangodb/async/ArangoDBAsync.java +++ b/core/src/main/java/com/arangodb/async/ArangoDBAsync.java @@ -305,6 +305,7 @@ private AsyncProtocolProvider asyncProtocolProvider(Protocol protocol) { if (p.supportsProtocol(protocol)) { return p; } + LOG.debug("Required protocol ({}) not supported by ProtocolProvider: {}", protocol, p.getClass().getName()); } throw new ArangoDBException("No ProtocolProvider found for protocol: " + protocol); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java index e482e405c..3ff36604b 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java @@ -44,7 +44,7 @@ * @author Mark Vollmary */ public abstract class InternalArangoDBBuilder> { - private static final Logger LOG = LoggerFactory.getLogger(InternalArangoDBBuilder.class); + protected static final Logger LOG = LoggerFactory.getLogger(InternalArangoDBBuilder.class); protected final ArangoConfig config = new ArangoConfig(); @SuppressWarnings("unchecked") @@ -295,6 +295,7 @@ protected ProtocolProvider protocolProvider(Protocol protocol) { if (p.supportsProtocol(protocol)) { return p; } + LOG.debug("Required protocol ({}) not supported by ProtocolProvider: {}", protocol, p.getClass().getName()); } throw new ArangoDBException("No ProtocolProvider found for protocol: " + protocol); } diff --git a/core/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java b/core/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java index 241e0893d..df088418e 100644 --- a/core/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java +++ b/core/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java @@ -4,11 +4,15 @@ import com.arangodb.ContentType; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ServiceLoader; import java.util.function.Supplier; public interface InternalMapperProvider extends Supplier { + Logger LOG = LoggerFactory.getLogger(InternalMapperProvider.class); + static ObjectMapper of(final ContentType contentType) { String formatName; if (contentType == ContentType.JSON) { @@ -24,6 +28,7 @@ static ObjectMapper of(final ContentType contentType) { if(formatName.equals(jf.getFormatName())){ return new ObjectMapper(jf); } + LOG.debug("Required format ({}) not supported by JsonFactory: {}", formatName, jf.getClass().getName()); } throw new ArangoDBException("No JsonFactory found for content type: " + contentType); diff --git a/driver/pom.xml b/driver/pom.xml index 3481759bd..003ef92fc 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -138,12 +138,12 @@ com.arangodb jackson-serde-vpack - test + true com.arangodb vst-protocol - test + true org.reflections @@ -154,7 +154,7 @@ org.graalvm.sdk graal-sdk - 22.3.0 + 22.3.1 test diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/native-image.properties b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/native-image.properties index 60c5b90d2..5f368dc7b 100644 --- a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/native-image.properties +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/native-image.properties @@ -1,9 +1,13 @@ Args=\ --H:ResourceConfigurationResources=${.}/resource-config.json \ --H:ReflectionConfigurationResources=${.}/reflect-config.json \ +-H:ResourceConfigurationResources=${.}/resource-config.json,${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config.json,${.}/reflect-config-spi.json,${.}/reflect-config-mp-config.json \ -H:SerializationConfigurationResources=${.}/serialization-config.json \ +--initialize-at-build-time=\ + org.slf4j \ --initialize-at-run-time=\ - io.netty.handler.ssl.BouncyCastleAlpnSslUtils,\ - io.netty.handler.codec.compression.ZstdOptions \ + io.netty.handler.ssl.BouncyCastleAlpnSslUtils,\ + io.netty.handler.codec.compression.ZstdOptions,\ + io.netty.handler.codec.compression.BrotliOptions,\ + io.netty.handler.codec.compression.Brotli \ -Dio.netty.noUnsafe=true \ -Dio.netty.leakDetection.level=DISABLED diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-mp-config.json b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-mp-config.json new file mode 100644 index 000000000..84892c37d --- /dev/null +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-mp-config.json @@ -0,0 +1,13 @@ +[ + { + "name": "com.arangodb.config.HostDescription", + "methods": [ + { + "name": "parse", + "parameterTypes": [ + "java.lang.CharSequence" + ] + } + ] + } +] \ No newline at end of file diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-spi.json b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-spi.json new file mode 100644 index 000000000..cf082f1dd --- /dev/null +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config-spi.json @@ -0,0 +1,11 @@ +[ + { + "name": "com.fasterxml.jackson.core.JsonFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/resource-config-spi.json b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/resource-config-spi.json new file mode 100644 index 000000000..7fb12eab0 --- /dev/null +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/resource-config-spi.json @@ -0,0 +1,10 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.fasterxml.jackson.core.JsonFactory" + } + ] + }, + "bundles": [] +} diff --git a/driver/src/test/java/com/arangodb/example/FirstProject.java b/driver/src/test/java/com/arangodb/example/FirstProject.java index 454bfabc7..cd0f7fc2c 100644 --- a/driver/src/test/java/com/arangodb/example/FirstProject.java +++ b/driver/src/test/java/com/arangodb/example/FirstProject.java @@ -1,6 +1,7 @@ package com.arangodb.example; import com.arangodb.*; +import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.BaseDocument; import com.arangodb.entity.CollectionEntity; import com.fasterxml.jackson.databind.JsonNode; @@ -12,7 +13,7 @@ public class FirstProject { public static void main(final String[] args) { - final ArangoDB arangoDB = new ArangoDB.Builder().user("root").build(); + final ArangoDB arangoDB = new ArangoDB.Builder().loadProperties(ArangoConfigProperties.fromFile()).user("root").build(); // create database final DbName dbName = DbName.of("mydb"); diff --git a/driver/src/test/java/graal/EmptyByteBufStub.java b/driver/src/test/java/graal/EmptyByteBufStub.java new file mode 100644 index 000000000..2f46d7c5c --- /dev/null +++ b/driver/src/test/java/graal/EmptyByteBufStub.java @@ -0,0 +1,33 @@ +package graal; + +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/driver/src/test/java/graal/HttpContentCompressorSubstitutions.java b/driver/src/test/java/graal/HttpContentCompressorSubstitutions.java new file mode 100644 index 000000000..92bed76be --- /dev/null +++ b/driver/src/test/java/graal/HttpContentCompressorSubstitutions.java @@ -0,0 +1,29 @@ +package 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; + +public class HttpContentCompressorSubstitutions { + + @TargetClass(className = "io.netty.handler.codec.compression.ZstdEncoder") + 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(); + } + } + +} diff --git a/driver/src/test/java/graal/NettySubstitutions.java b/driver/src/test/java/graal/NettySubstitutions.java new file mode 100644 index 000000000..899d03616 --- /dev/null +++ b/driver/src/test/java/graal/NettySubstitutions.java @@ -0,0 +1,605 @@ +package 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 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: " + 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!"); + + @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/driver/src/test/java/graal/VertxSubstitutions.java b/driver/src/test/java/graal/VertxSubstitutions.java new file mode 100644 index 000000000..2127e16f0 --- /dev/null +++ b/driver/src/test/java/graal/VertxSubstitutions.java @@ -0,0 +1,195 @@ +package 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.net.NetServerOptions; +import io.vertx.core.net.impl.transport.Transport; +import io.vertx.core.spi.resolver.ResolverProvider; + +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.net.impl.transport.Transport") +final class Target_io_vertx_core_net_impl_transport_Transport { + @Substitute + public static Transport nativeTransport() { + return Transport.JDK; + } +} + +/** + * 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, 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/driver/src/test/java/graal/ZLibSubstitutions.java b/driver/src/test/java/graal/ZLibSubstitutions.java new file mode 100644 index 000000000..515cf51e9 --- /dev/null +++ b/driver/src/test/java/graal/ZLibSubstitutions.java @@ -0,0 +1,66 @@ +package 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/driver/src/test/java/graalvm/UnicodeUtilsTest.java b/driver/src/test/java/unicode/UnicodeUtilsTest.java similarity index 100% rename from driver/src/test/java/graalvm/UnicodeUtilsTest.java rename to driver/src/test/java/unicode/UnicodeUtilsTest.java diff --git a/driver/src/test/resources/META-INF/native-image/native-image.properties b/driver/src/test/resources/META-INF/native-image/native-image.properties index b039368e7..7a9968e8f 100644 --- a/driver/src/test/resources/META-INF/native-image/native-image.properties +++ b/driver/src/test/resources/META-INF/native-image/native-image.properties @@ -3,6 +3,4 @@ Args=\ -H:ReflectionConfigurationResources=${.}/reflect-config.json \ -H:SerializationConfigurationResources=${.}/serialization-config.json \ --initialize-at-build-time=\ - ch.qos.logback,\ - org.slf4j,\ org.junit.platform.engine.TestTag diff --git a/driver/src/test/resources/META-INF/native-image/reflect-config.json b/driver/src/test/resources/META-INF/native-image/reflect-config.json index e8a1f86bc..904da3577 100644 --- a/driver/src/test/resources/META-INF/native-image/reflect-config.json +++ b/driver/src/test/resources/META-INF/native-image/reflect-config.json @@ -12,80 +12,6 @@ } ] }, - { - "name": "ch.qos.logback.classic.encoder.PatternLayoutEncoder", - "allPublicMethods": true, - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.DateConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.LevelConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.LineSeparatorConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.LoggerConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.MessageConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.classic.pattern.ThreadConverter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ch.qos.logback.core.ConsoleAppender", - "allPublicMethods": true, - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, { "name": "com.arangodb.ArangoCollectionTest$TestUpdateEntity", "allDeclaredFields":true, diff --git a/driver/src/test/resources/logback-test.xml b/driver/src/test/resources/logback-test.xml deleted file mode 100644 index b1d65b196..000000000 --- a/driver/src/test/resources/logback-test.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - diff --git a/driver/src/test/resources/simplelogger.properties b/driver/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..250d7c6b1 --- /dev/null +++ b/driver/src/test/resources/simplelogger.properties @@ -0,0 +1,10 @@ +org.slf4j.simpleLogger.logFile=System.out +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS +org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false + + +org.slf4j.simpleLogger.defaultLogLevel=info +#org.slf4j.simpleLogger.log.com.arangodb.http.HttpCommunication=debug diff --git a/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/native-image.properties b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/native-image.properties new file mode 100644 index 000000000..f6d4bf39a --- /dev/null +++ b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/native-image.properties @@ -0,0 +1,3 @@ +Args=\ +-H:ResourceConfigurationResources=${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config-spi.json diff --git a/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/reflect-config-spi.json b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/reflect-config-spi.json new file mode 100644 index 000000000..be5c8f170 --- /dev/null +++ b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/reflect-config-spi.json @@ -0,0 +1,11 @@ +[ + { + "name": "com.arangodb.http.HttpProtocolProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/resource-config-spi.json b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/resource-config-spi.json new file mode 100644 index 000000000..9037d85e5 --- /dev/null +++ b/http/src/main/resources/META-INF/native-image/com.arangodb/http-protocol/resource-config-spi.json @@ -0,0 +1,10 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.arangodb.internal.net.ProtocolProvider" + } + ] + }, + "bundles": [] +} diff --git a/shaded-integration-tests/pom.xml b/integration-tests/pom.xml similarity index 74% rename from shaded-integration-tests/pom.xml rename to integration-tests/pom.xml index 2c7e8da7f..47f9b220f 100644 --- a/shaded-integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -10,7 +10,7 @@ 7.0.0-ALPHA.2 - shaded-integration-tests + integration-tests 17 @@ -29,6 +29,39 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-bytecode-version + + enforce + + + + + 1.8 + + com.tngtech.archunit:archunit + org.eclipse:yasson + jakarta.json.bind:jakarta.json.bind-api + + + + true + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.6.1 + + + @@ -145,7 +178,7 @@ - ${project.basedir}/src/test/native/java + ${project.basedir}/src/test/plain/java ** ${project.build.directory}/generated-test-sources replacer @@ -185,6 +218,35 @@ + + plain + + src/test/plain/java + jackson + + + + com.arangodb + arangodb-java-driver + + + com.arangodb + http-protocol + + + com.arangodb + vst-protocol + + + com.arangodb + jackson-serde-json + + + com.arangodb + jackson-serde-vpack + + + \ No newline at end of file diff --git a/shaded-integration-tests/src/test/internal/java/arch/AdbTest.java b/integration-tests/src/test/internal/java/arch/AdbTest.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/AdbTest.java rename to integration-tests/src/test/internal/java/arch/AdbTest.java diff --git a/shaded-integration-tests/src/test/internal/java/arch/BaseTest.java b/integration-tests/src/test/internal/java/arch/BaseTest.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/BaseTest.java rename to integration-tests/src/test/internal/java/arch/BaseTest.java diff --git a/shaded-integration-tests/src/test/internal/java/arch/ConfigUtils.java b/integration-tests/src/test/internal/java/arch/ConfigUtils.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/ConfigUtils.java rename to integration-tests/src/test/internal/java/arch/ConfigUtils.java diff --git a/shaded-integration-tests/src/test/internal/java/arch/Person.java b/integration-tests/src/test/internal/java/arch/Person.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/Person.java rename to integration-tests/src/test/internal/java/arch/Person.java diff --git a/shaded-integration-tests/src/test/internal/java/arch/RelocationsTest.java b/integration-tests/src/test/internal/java/arch/RelocationsTest.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/RelocationsTest.java rename to integration-tests/src/test/internal/java/arch/RelocationsTest.java diff --git a/shaded-integration-tests/src/test/internal/java/arch/SerdeTest.java b/integration-tests/src/test/internal/java/arch/SerdeTest.java similarity index 100% rename from shaded-integration-tests/src/test/internal/java/arch/SerdeTest.java rename to integration-tests/src/test/internal/java/arch/SerdeTest.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/AdbTest.java b/integration-tests/src/test/jackson/java/arch/AdbTest.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/AdbTest.java rename to integration-tests/src/test/jackson/java/arch/AdbTest.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/BaseTest.java b/integration-tests/src/test/jackson/java/arch/BaseTest.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/BaseTest.java rename to integration-tests/src/test/jackson/java/arch/BaseTest.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/ConfigUtils.java b/integration-tests/src/test/jackson/java/arch/ConfigUtils.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/ConfigUtils.java rename to integration-tests/src/test/jackson/java/arch/ConfigUtils.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/Person.java b/integration-tests/src/test/jackson/java/arch/Person.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/Person.java rename to integration-tests/src/test/jackson/java/arch/Person.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java b/integration-tests/src/test/jackson/java/arch/RelocationsTest.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java rename to integration-tests/src/test/jackson/java/arch/RelocationsTest.java diff --git a/shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java b/integration-tests/src/test/jackson/java/arch/SerdeTest.java similarity index 100% rename from shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java rename to integration-tests/src/test/jackson/java/arch/SerdeTest.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/AdbTest.java b/integration-tests/src/test/jsonb/java/arch/AdbTest.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/AdbTest.java rename to integration-tests/src/test/jsonb/java/arch/AdbTest.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/BaseTest.java b/integration-tests/src/test/jsonb/java/arch/BaseTest.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/BaseTest.java rename to integration-tests/src/test/jsonb/java/arch/BaseTest.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/ConfigUtils.java b/integration-tests/src/test/jsonb/java/arch/ConfigUtils.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/ConfigUtils.java rename to integration-tests/src/test/jsonb/java/arch/ConfigUtils.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/Person.java b/integration-tests/src/test/jsonb/java/arch/Person.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/Person.java rename to integration-tests/src/test/jsonb/java/arch/Person.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java b/integration-tests/src/test/jsonb/java/arch/RelocationsTest.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java rename to integration-tests/src/test/jsonb/java/arch/RelocationsTest.java diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/SerdeTest.java b/integration-tests/src/test/jsonb/java/arch/SerdeTest.java similarity index 100% rename from shaded-integration-tests/src/test/jsonb/java/arch/SerdeTest.java rename to integration-tests/src/test/jsonb/java/arch/SerdeTest.java diff --git a/shaded-integration-tests/src/test/native/java/com b/integration-tests/src/test/plain/java/com similarity index 100% rename from shaded-integration-tests/src/test/native/java/com rename to integration-tests/src/test/plain/java/com diff --git a/shaded-integration-tests/src/test/resources b/integration-tests/src/test/resources similarity index 100% rename from shaded-integration-tests/src/test/resources rename to integration-tests/src/test/resources diff --git a/jackson-serde-json/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java b/jackson-serde-json/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java index 936192d0e..e4cca8d11 100644 --- a/jackson-serde-json/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java +++ b/jackson-serde-json/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java @@ -4,6 +4,8 @@ import com.arangodb.ContentType; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ServiceLoader; import java.util.function.Supplier; @@ -12,6 +14,8 @@ * Not shaded in arangodb-java-driver-shaded. */ public interface JacksonMapperProvider extends Supplier { + Logger LOG = LoggerFactory.getLogger(JacksonMapperProvider.class); + static ObjectMapper of(final ContentType contentType) { String formatName; if (contentType == ContentType.JSON) { @@ -27,6 +31,7 @@ static ObjectMapper of(final ContentType contentType) { if(formatName.equals(jf.getFormatName())){ return new ObjectMapper(jf); } + LOG.debug("Required format ({}) not supported by JsonFactory: {}", formatName, jf.getClass().getName()); } throw new ArangoDBException("No JsonFactory found for content type: " + contentType); diff --git a/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/native-image.properties b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/native-image.properties new file mode 100644 index 000000000..f6d4bf39a --- /dev/null +++ b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/native-image.properties @@ -0,0 +1,3 @@ +Args=\ +-H:ResourceConfigurationResources=${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config-spi.json diff --git a/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/reflect-config-spi.json b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/reflect-config-spi.json new file mode 100644 index 000000000..cee7b71af --- /dev/null +++ b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/reflect-config-spi.json @@ -0,0 +1,11 @@ +[ + { + "name": "com.arangodb.serde.jackson.JacksonSerdeProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/resource-config-spi.json b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/resource-config-spi.json new file mode 100644 index 000000000..520213dcf --- /dev/null +++ b/jackson-serde-json/src/main/resources/META-INF/native-image/com.arangodb/jackson-serde-json/resource-config-spi.json @@ -0,0 +1,10 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.arangodb.serde.ArangoSerdeProvider" + } + ] + }, + "bundles": [] +} diff --git a/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/native-image.properties b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/native-image.properties new file mode 100644 index 000000000..f6d4bf39a --- /dev/null +++ b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/native-image.properties @@ -0,0 +1,3 @@ +Args=\ +-H:ResourceConfigurationResources=${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config-spi.json diff --git a/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/reflect-config-spi.json b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/reflect-config-spi.json new file mode 100644 index 000000000..5a63b3c30 --- /dev/null +++ b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/reflect-config-spi.json @@ -0,0 +1,11 @@ +[ + { + "name": "com.arangodb.serde.jsonb.JsonbSerdeProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/resource-config-spi.json b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/resource-config-spi.json new file mode 100644 index 000000000..520213dcf --- /dev/null +++ b/jsonb-serde/src/main/resources/META-INF/native-image/com.arangodb/json-serde/resource-config-spi.json @@ -0,0 +1,10 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.arangodb.serde.ArangoSerdeProvider" + } + ] + }, + "bundles": [] +} diff --git a/pom.xml b/pom.xml index 24508f88f..1cf00c101 100644 --- a/pom.xml +++ b/pom.xml @@ -61,8 +61,8 @@ - ch.qos.logback - logback-classic + org.slf4j + slf4j-simple test @@ -156,17 +156,17 @@ com.arangodb jackson-dataformat-velocypack - 4.0.0 + 4.0.1 org.slf4j slf4j-api - 2.0.5 + 2.0.6 - ch.qos.logback - logback-classic - 1.3.5 + org.slf4j + slf4j-simple + 2.0.6 org.assertj @@ -429,6 +429,7 @@ false --no-fallback + --link-at-build-time ${SslTest} @@ -437,6 +438,20 @@ + + + com.aayushatharva.brotli4j + brotli4j + 1.11.0 + test + + + javax.annotation + javax.annotation-api + 1.3.2 + test + + diff --git a/resilience-tests/pom.xml b/resilience-tests/pom.xml index 8d379f9bf..53371a117 100644 --- a/resilience-tests/pom.xml +++ b/resilience-tests/pom.xml @@ -35,6 +35,11 @@ awaitility 4.2.0 + + ch.qos.logback + logback-classic + 1.3.5 + \ No newline at end of file diff --git a/shaded/pom.xml b/shaded/pom.xml index 40db119e9..a04e5f863 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -40,6 +40,12 @@ org.slf4j slf4j-api + + org.graalvm.sdk + graal-sdk + 22.3.1 + provided + @@ -94,7 +100,8 @@ com.arangodb:vst-protocol - META-INF/** + META-INF/MANIFEST.MF + META-INF/services/** diff --git a/shaded/src/main/java/graal/BrotliSubstitutions.java b/shaded/src/main/java/graal/BrotliSubstitutions.java new file mode 100644 index 000000000..fee8b14a1 --- /dev/null +++ b/shaded/src/main/java/graal/BrotliSubstitutions.java @@ -0,0 +1,22 @@ +package graal; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + + +@TargetClass(className = "io.netty.handler.codec.compression.Brotli") +final class Target_com_arangodb_shaded_netty_handler_codec_compression_Brotli { + @Substitute + public static boolean isAvailable() { + return false; + } + + @Substitute + public static void ensureAvailability() throws Throwable { + throw new UnsupportedOperationException(); + } +} + +public class BrotliSubstitutions { + +} diff --git a/shaded/src/main/java/graal/EmptyByteBufStub.java b/shaded/src/main/java/graal/EmptyByteBufStub.java new file mode 100644 index 000000000..2f46d7c5c --- /dev/null +++ b/shaded/src/main/java/graal/EmptyByteBufStub.java @@ -0,0 +1,33 @@ +package graal; + +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/shaded/src/main/java/graal/HttpContentCompressorSubstitutions.java b/shaded/src/main/java/graal/HttpContentCompressorSubstitutions.java new file mode 100644 index 000000000..92bed76be --- /dev/null +++ b/shaded/src/main/java/graal/HttpContentCompressorSubstitutions.java @@ -0,0 +1,29 @@ +package 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; + +public class HttpContentCompressorSubstitutions { + + @TargetClass(className = "io.netty.handler.codec.compression.ZstdEncoder") + 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(); + } + } + +} diff --git a/shaded/src/main/java/graal/NettySubstitutions.java b/shaded/src/main/java/graal/NettySubstitutions.java new file mode 100644 index 000000000..856420318 --- /dev/null +++ b/shaded/src/main/java/graal/NettySubstitutions.java @@ -0,0 +1,605 @@ +package 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 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_com_arangodb_shaded_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_com_arangodb_shaded_netty_handler_ssl_SslProvider { + @Substitute + public static boolean isAlpnSupported(final SslProvider provider) { + switch (provider) { + case JDK: + return Target_com_arangodb_shaded_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator.isAlpnSupported(); + case OPENSSL: + case OPENSSL_REFCNT: + return false; + default: + throw new Error("SslProvider unsupported: " + provider); + } + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator") +final class Target_com_arangodb_shaded_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_com_arangodb_shaded_netty_handler_ssl_OpenSsl { + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static Throwable UNAVAILABILITY_CAUSE = new RuntimeException("OpenSsl unsupported!"); + + @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_com_arangodb_shaded_netty_handler_ssl_JdkSslServerContext { + + @Alias + Target_com_arangodb_shaded_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_com_arangodb_shaded_netty_handler_ssl_JdkSslClientContext { + + @Alias + Target_com_arangodb_shaded_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_com_arangodb_shaded_netty_handler_ssl_SslHandler$SslEngineType { + + @Alias + public static Target_com_arangodb_shaded_netty_handler_ssl_SslHandler$SslEngineType JDK; + + @Substitute + static Target_com_arangodb_shaded_netty_handler_ssl_SslHandler$SslEngineType forEngine(SSLEngine engine) { + return JDK; + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator$AlpnWrapper") +final class Target_com_arangodb_shaded_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator_AlpnWrapper { + @Substitute + public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, + JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { + return (SSLEngine) (Object) new Target_com_arangodb_shaded_netty_handler_ssl_JdkAlpnSslEngine(engine, applicationNegotiator, + isServer); + } + +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnSslEngine") +final class Target_com_arangodb_shaded_netty_handler_ssl_JdkAlpnSslEngine { + @Alias + Target_com_arangodb_shaded_netty_handler_ssl_JdkAlpnSslEngine(final SSLEngine engine, + final JdkApplicationProtocolNegotiator applicationNegotiator, final boolean isServer) { + + } +} + +@TargetClass(className = "io.netty.handler.ssl.SslContext") +final class Target_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator { + + @Alias + public static Target_com_arangodb_shaded_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator INSTANCE; +} + +@TargetClass(className = "io.netty.handler.ssl.JdkSslContext") +final class Target_com_arangodb_shaded_netty_handler_ssl_JdkSslContext { + + @Substitute + static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) { + if (config == null) { + return (JdkApplicationProtocolNegotiator) (Object) Target_com_arangodb_shaded_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator.INSTANCE; + } + + switch (config.protocol()) { + case NONE: + return (JdkApplicationProtocolNegotiator) (Object) Target_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_netty_channel_nio_NioEventLoop { + + @Substitute + private static Queue newTaskQueue0(int maxPendingTasks) { + return new LinkedBlockingDeque<>(); + } +} + +@TargetClass(className = "io.netty.buffer.AbstractReferenceCountedByteBuf") +final class Target_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_netty_util_concurrent_ScheduledFutureTask { + static final long START_TIME = System.nanoTime(); +} + +@TargetClass(className = "io.netty.util.concurrent.AbstractScheduledEventExecutor") +final class Target_com_arangodb_shaded_netty_util_concurrent_AbstractScheduledEventExecutor { + + // The START_TIME field is kept but not used. + // All the accesses to it have been replaced with Holder_com_arangodb_shaded_netty_util_concurrent_ScheduledFutureTask + + @Substitute + static long initialNanoTime() { + return Holder_com_arangodb_shaded_netty_util_concurrent_ScheduledFutureTask.START_TIME; + } + + @Substitute + static long defaultCurrentTimeNanos() { + return System.nanoTime() - Holder_com_arangodb_shaded_netty_util_concurrent_ScheduledFutureTask.START_TIME; + } +} + +@TargetClass(className = "io.netty.channel.ChannelHandlerMask") +final class Target_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_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_com_arangodb_shaded_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/shaded/src/main/java/graal/VertxSubstitutions.java b/shaded/src/main/java/graal/VertxSubstitutions.java new file mode 100644 index 000000000..14892fc8f --- /dev/null +++ b/shaded/src/main/java/graal/VertxSubstitutions.java @@ -0,0 +1,195 @@ +package 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.net.NetServerOptions; +import io.vertx.core.net.impl.transport.Transport; +import io.vertx.core.spi.resolver.ResolverProvider; + +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.net.impl.transport.Transport") +final class Target_com_arangodb_shaded_vertx_core_net_impl_transport_Transport { + @Substitute + public static Transport nativeTransport() { + return Transport.JDK; + } +} + +/** + * 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_com_arangodb_shaded_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_com_arangodb_shaded_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, 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_com_arangodb_shaded_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_com_arangodb_shaded_vertx_core_spi_tls_DefaultJDKCipherSuite { + @Alias + static List get() { + return null; + } +} + +class VertxSubstitutions { + +} diff --git a/shaded/src/main/java/graal/ZLibSubstitutions.java b/shaded/src/main/java/graal/ZLibSubstitutions.java new file mode 100644 index 000000000..af131b702 --- /dev/null +++ b/shaded/src/main/java/graal/ZLibSubstitutions.java @@ -0,0 +1,66 @@ +package 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_com_arangodb_shaded_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/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/native-image.properties b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/native-image.properties index ecbea5c1b..8a2425d56 100644 --- a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/native-image.properties +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/native-image.properties @@ -1,9 +1,39 @@ Args=\ --H:ResourceConfigurationResources=${.}/resource-config.json \ --H:ReflectionConfigurationResources=${.}/reflect-config.json,${.}/netty-reflect-config.json \ +-H:ResourceConfigurationResources=${.}/resource-config.json,${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config.json,${.}/reflect-config-spi.json,${.}/reflect-config-mp-config.json,${.}/reflect-config-netty.json \ -H:SerializationConfigurationResources=${.}/serialization-config.json \ ---initialize-at-run-time=\ - com.arangodb.shaded.netty.handler.ssl.BouncyCastleAlpnSslUtils,\ - com.arangodb.shaded.netty.handler.codec.compression.ZstdOptions \ -Dcom.arangodb.shaded.netty.noUnsafe=true \ --Dcom.arangodb.shaded.netty.leakDetection.level=DISABLED +-Dcom.arangodb.shaded.netty.leakDetection.level=DISABLED \ +--initialize-at-build-time=\ + org.slf4j,\ + com.arangodb.shaded.netty \ +--initialize-at-run-time=\ + com.arangodb.shaded.netty.buffer.AbstractReferenceCountedByteBuf,\ + com.arangodb.shaded.netty.buffer.ByteBufAllocator,\ + com.arangodb.shaded.netty.buffer.ByteBufUtil,\ + com.arangodb.shaded.netty.buffer.PooledByteBufAllocator,\ + com.arangodb.shaded.netty.handler.codec.compression.Brotli,\ + com.arangodb.shaded.netty.handler.codec.compression.BrotliDecoder,\ + com.arangodb.shaded.netty.handler.codec.compression.BrotliOptions,\ + com.arangodb.shaded.netty.handler.codec.compression.ZstdOptions,\ + com.arangodb.shaded.netty.handler.codec.http.HttpObjectEncoder,\ + com.arangodb.shaded.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder,\ + com.arangodb.shaded.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,\ + com.arangodb.shaded.netty.handler.codec.http2.DefaultHttp2FrameWriter,\ + com.arangodb.shaded.netty.handler.codec.http2.Http2ClientUpgradeCodec,\ + com.arangodb.shaded.netty.handler.codec.http2.Http2CodecUtil,\ + com.arangodb.shaded.netty.handler.codec.http2.Http2ConnectionHandler,\ + com.arangodb.shaded.netty.handler.ssl.BouncyCastleAlpnSslUtils,\ + com.arangodb.shaded.netty.handler.ssl.util.ThreadLocalInsecureRandom,\ + com.arangodb.shaded.netty.resolver.dns.DefaultDnsServerAddressStreamProvider,\ + com.arangodb.shaded.netty.resolver.dns.DnsNameResolver,\ + com.arangodb.shaded.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder,\ + com.arangodb.shaded.netty.resolver.HostsFileEntriesResolver,\ + com.arangodb.shaded.netty.util.AbstractReferenceCounted,\ + com.arangodb.shaded.netty.util.concurrent.GlobalEventExecutor,\ + com.arangodb.shaded.netty.util.concurrent.ImmediateEventExecutor,\ + com.arangodb.shaded.netty.util.concurrent.ScheduledFutureTask,\ + com.arangodb.shaded.netty.util.internal.ThreadLocalRandom,\ + com.arangodb.shaded.netty.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder,\ + com.arangodb.shaded.netty.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder,\ + com.arangodb.shaded.netty.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-mp-config.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-mp-config.json new file mode 100644 index 000000000..84892c37d --- /dev/null +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-mp-config.json @@ -0,0 +1,13 @@ +[ + { + "name": "com.arangodb.config.HostDescription", + "methods": [ + { + "name": "parse", + "parameterTypes": [ + "java.lang.CharSequence" + ] + } + ] + } +] \ No newline at end of file diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/netty-reflect-config.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-netty.json similarity index 100% rename from shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/netty-reflect-config.json rename to shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-netty.json diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-spi.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-spi.json new file mode 100644 index 000000000..e14d4bf2e --- /dev/null +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config-spi.json @@ -0,0 +1,20 @@ +[ + { + "name": "com.arangodb.shaded.fasterxml.jackson.core.JsonFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.arangodb.shaded.jackson.dataformat.velocypack.VPackFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/resource-config-spi.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/resource-config-spi.json new file mode 100644 index 000000000..43406b15c --- /dev/null +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/resource-config-spi.json @@ -0,0 +1,10 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.arangodb.shaded.fasterxml.jackson.core.JsonFactory" + } + ] + }, + "bundles": [] +} diff --git a/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/native-image.properties b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/native-image.properties new file mode 100644 index 000000000..f6d4bf39a --- /dev/null +++ b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/native-image.properties @@ -0,0 +1,3 @@ +Args=\ +-H:ResourceConfigurationResources=${.}/resource-config-spi.json \ +-H:ReflectionConfigurationResources=${.}/reflect-config-spi.json diff --git a/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/reflect-config-spi.json b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/reflect-config-spi.json new file mode 100644 index 000000000..c582132f6 --- /dev/null +++ b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/reflect-config-spi.json @@ -0,0 +1,20 @@ +[ + { + "name": "com.arangodb.vst.VstAsyncProtocolProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.arangodb.vst.VstProtocolProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/resource-config-spi.json b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/resource-config-spi.json new file mode 100644 index 000000000..b4ecc2447 --- /dev/null +++ b/vst/src/main/resources/META-INF/native-image/com.arangodb/vst-protocol/resource-config-spi.json @@ -0,0 +1,13 @@ +{ + "resources": { + "includes": [ + { + "pattern": "META-INF/services/com.arangodb.internal.net.AsyncProtocolProvider" + }, + { + "pattern": "META-INF/services/com.arangodb.internal.net.ProtocolProvider" + } + ] + }, + "bundles": [] +}