From 669d441f6074e6946a3a5e62b42c26a34eb76973 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 29 May 2024 09:18:30 +0200 Subject: [PATCH 1/6] updated arangodb-java-driver v7.7.0 --- .github/workflows/maven.yml | 35 ++++++++++++++++++++ docker/start_db.sh | 1 + pom.xml | 2 +- src/main/kotlin/com/example/ArangoService.kt | 2 ++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..70e5e25 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,35 @@ +name: Native CI + +on: push + +jobs: + + verify: + timeout-minutes: 10 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: graalvm/setup-graalvm@v1 + with: + java-version: '22.0.1' + distribution: 'graalvm-community' + cache: 'maven' + native-image-job-reports: 'true' + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Info + run: mvn -version + - name: Start Database + run: ./docker/start_db.sh + - name: test + run: mvn test + - name: package-native + run: mvn package -Dpackaging=native-image + - name: start-native + run: ./target/demo & + - name: wait + run: sleep 1 + - name: test-version + run: curl -v --fail http://localhost:8080/version + - name: test-version + run: curl -v --fail http://localhost:8080/order diff --git a/docker/start_db.sh b/docker/start_db.sh index 5515382..619abc8 100755 --- a/docker/start_db.sh +++ b/docker/start_db.sh @@ -66,6 +66,7 @@ docker run -d \ -e ARANGO_LICENSE_KEY="$ARANGO_LICENSE_KEY" \ $STARTER_DOCKER_IMAGE \ $STARTER_ARGS \ + --docker.net-mode=default \ --docker.container=adb \ --auth.jwt-secret=/jwtSecret \ --starter.address="${GW}" \ diff --git a/pom.xml b/pom.xml index 4f1d34b..5772b27 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ com.arangodb arangodb-java-driver - 7.6.0 + 7.7.0-SNAPSHOT io.micronaut diff --git a/src/main/kotlin/com/example/ArangoService.kt b/src/main/kotlin/com/example/ArangoService.kt index aebeea3..8e31dc8 100644 --- a/src/main/kotlin/com/example/ArangoService.kt +++ b/src/main/kotlin/com/example/ArangoService.kt @@ -12,6 +12,8 @@ class ArangoService(config: ArangoConfig, mapper: ObjectMapper) { private val adb: ArangoDB = ArangoDB.Builder() .loadProperties(ArangoConfigAdapter(config)) + + // ArangoSerde implementation based on Micronaut serialization .serde(object : ArangoSerde { override fun serialize(value: Any?): ByteArray { return mapper.writeValueAsBytes(value) From 9614a5be4602e18436464bb560eecb4fb83730bb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Jun 2024 16:23:44 +0200 Subject: [PATCH 2/6] added shaded driver profile --- .github/workflows/maven.yml | 17 +++++-- README.md | 17 ++++++- pom.xml | 39 ++++++++++++++-- src/main/kotlin/com/example/ArangoConfig.kt | 19 ++++++++ src/main/kotlin/com/example/ArangoProvider.kt | 44 ++++++++++++++++++ .../{HelloController.kt => ArangoResource.kt} | 2 +- src/main/kotlin/com/example/ArangoService.kt | 19 +------- .../com/example/demo/native-image.properties | 5 ++ .../META-INF/resources/adb.truststore | Bin 0 -> 2251 bytes src/main/resources/application.properties | 6 ++- 10 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/com/example/ArangoProvider.kt rename src/main/kotlin/com/example/{HelloController.kt => ArangoResource.kt} (90%) create mode 100644 src/main/resources/META-INF/native-image/com/example/demo/native-image.properties create mode 100644 src/main/resources/META-INF/resources/adb.truststore diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 70e5e25..000e02b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,28 +8,39 @@ jobs: timeout-minutes: 10 runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shaded: + - 'true' + - 'false' + steps: - uses: actions/checkout@v2 - uses: graalvm/setup-graalvm@v1 with: - java-version: '22.0.1' + java-version: '21.0.2' distribution: 'graalvm-community' cache: 'maven' native-image-job-reports: 'true' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Info run: mvn -version + - name: Deps tree + run: mvn dependency:tree -Dshaded=${{matrix.shaded}} - name: Start Database run: ./docker/start_db.sh + env: + SSL: true - name: test run: mvn test - name: package-native - run: mvn package -Dpackaging=native-image + run: mvn -Dpackaging=native-image -Dshaded=${{matrix.shaded}} package - name: start-native run: ./target/demo & - name: wait run: sleep 1 - name: test-version run: curl -v --fail http://localhost:8080/version - - name: test-version + - name: test-serde run: curl -v --fail http://localhost:8080/order diff --git a/README.md b/README.md index 3e5f8b5..5ce644b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Example application using ArangoDB Java driver integrated with: Start a local database: ```shell script -./docker/start_db.sh +SSL=true ./docker/start_db.sh ``` ## test @@ -19,6 +19,12 @@ Start a local database: mvn test ``` +## test shaded + +```shell script +mvn test -Dshaded +``` + ## native image ```shell script @@ -27,3 +33,12 @@ mvn package -Dpackaging=native-image curl -X GET http://localhost:8080/version curl -X GET http://localhost:8080/order ``` + +## native image shaded + +```shell script +mvn package -Dpackaging=native-image -Dshaded +./target/demo +curl -X GET http://localhost:8080/version +curl -X GET http://localhost:8080/order +``` diff --git a/pom.xml b/pom.xml index 5772b27..aef5b64 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ netty com.example.ApplicationKt 1.9.23 + 7.7.0-SNAPSHOT @@ -32,11 +33,6 @@ - - com.arangodb - arangodb-java-driver - 7.7.0-SNAPSHOT - io.micronaut micronaut-http-server-netty @@ -296,4 +292,37 @@ + + + plain + + + !shaded + + + + + com.arangodb + arangodb-java-driver + ${adb.version} + + + + + shaded + + + shaded + + + + + com.arangodb + arangodb-java-driver-shaded + ${adb.version} + + + + + diff --git a/src/main/kotlin/com/example/ArangoConfig.kt b/src/main/kotlin/com/example/ArangoConfig.kt index 1f63e7a..614a883 100644 --- a/src/main/kotlin/com/example/ArangoConfig.kt +++ b/src/main/kotlin/com/example/ArangoConfig.kt @@ -1,6 +1,7 @@ package com.example +import com.arangodb.Protocol import com.arangodb.config.ArangoConfigProperties import com.arangodb.config.HostDescription import io.micronaut.context.annotation.ConfigurationProperties @@ -10,6 +11,16 @@ import java.util.* class ArangoConfig { var hosts: Optional> = Optional.empty() var password: Optional = Optional.empty() + var protocol: Optional = Optional.empty() + var useSsl: Optional = Optional.empty() + var ssl: SslConfig = SslConfig() + + @ConfigurationProperties("ssl") + class SslConfig { + lateinit var trustStoreFile: String + lateinit var trustStorePassword: String + lateinit var trustStoreType: String + } } class ArangoConfigAdapter(private val config: ArangoConfig) : ArangoConfigProperties { @@ -20,4 +31,12 @@ class ArangoConfigAdapter(private val config: ArangoConfig) : ArangoConfigProper override fun getPassword(): Optional { return config.password } + + override fun getProtocol(): Optional { + return config.protocol + } + + override fun getUseSsl(): Optional { + return config.useSsl + } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/ArangoProvider.kt b/src/main/kotlin/com/example/ArangoProvider.kt new file mode 100644 index 0000000..cc97ebd --- /dev/null +++ b/src/main/kotlin/com/example/ArangoProvider.kt @@ -0,0 +1,44 @@ +package com.example + +import com.arangodb.ArangoDB +import com.arangodb.serde.ArangoSerde +import io.micronaut.context.annotation.Factory +import io.micronaut.serde.ObjectMapper +import jakarta.inject.Singleton +import java.security.KeyStore +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManagerFactory + +@Factory +class ArangoProvider { + @Singleton + fun arangoDB(config: ArangoConfig, mapper: ObjectMapper): ArangoDB = ArangoDB.Builder() + .loadProperties(ArangoConfigAdapter(config)) + .sslContext(createSslContext(config)) + + // ArangoSerde implementation based on Micronaut serialization + .serde(object : ArangoSerde { + override fun serialize(value: Any?): ByteArray { + return mapper.writeValueAsBytes(value) + } + + override fun deserialize(content: ByteArray, clazz: Class): T? { + return mapper.readValue(content, clazz) + } + }) + .build() + + + private fun createSslContext(config: ArangoConfig): SSLContext { + val ks = KeyStore.getInstance(config.ssl.trustStoreType) + ks.load( + Thread.currentThread().contextClassLoader.getResourceAsStream(config.ssl.trustStoreFile), + config.ssl.trustStorePassword.toCharArray() + ) + val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + tmf.init(ks) + val sc = SSLContext.getInstance("TLS") + sc.init(null, tmf.trustManagers, null) + return sc + } +} diff --git a/src/main/kotlin/com/example/HelloController.kt b/src/main/kotlin/com/example/ArangoResource.kt similarity index 90% rename from src/main/kotlin/com/example/HelloController.kt rename to src/main/kotlin/com/example/ArangoResource.kt index 5cd8336..41ad80e 100644 --- a/src/main/kotlin/com/example/HelloController.kt +++ b/src/main/kotlin/com/example/ArangoResource.kt @@ -10,7 +10,7 @@ import io.micronaut.serde.annotation.SerdeImport @Controller("/") @SerdeImport(ArangoDBVersion::class) -class HelloController(private val svc: ArangoService) { +class ArangoResource(private val svc: ArangoService) { @Get("/version") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/kotlin/com/example/ArangoService.kt b/src/main/kotlin/com/example/ArangoService.kt index 8e31dc8..ed119af 100644 --- a/src/main/kotlin/com/example/ArangoService.kt +++ b/src/main/kotlin/com/example/ArangoService.kt @@ -2,28 +2,11 @@ package com.example import com.arangodb.ArangoDB import com.arangodb.entity.ArangoDBVersion -import com.arangodb.serde.ArangoSerde -import io.micronaut.serde.ObjectMapper import jakarta.inject.Singleton import java.util.* @Singleton -class ArangoService(config: ArangoConfig, mapper: ObjectMapper) { - - private val adb: ArangoDB = ArangoDB.Builder() - .loadProperties(ArangoConfigAdapter(config)) - - // ArangoSerde implementation based on Micronaut serialization - .serde(object : ArangoSerde { - override fun serialize(value: Any?): ByteArray { - return mapper.writeValueAsBytes(value) - } - - override fun deserialize(content: ByteArray, clazz: Class): T? { - return mapper.readValue(content, clazz) - } - }) - .build() +class ArangoService(private val adb: ArangoDB) { fun getVersion(): ArangoDBVersion { return adb.version diff --git a/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties new file mode 100644 index 0000000..c495ad1 --- /dev/null +++ b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties @@ -0,0 +1,5 @@ +Args=\ +--initialize-at-build-time=\ + com.example,\ + ch.qos.logback,\ + org.slf4j diff --git a/src/main/resources/META-INF/resources/adb.truststore b/src/main/resources/META-INF/resources/adb.truststore new file mode 100644 index 0000000000000000000000000000000000000000..e683a48b8a25067c921385081a3a930eae74f05a GIT binary patch literal 2251 zcmchYc{J1w7sux}E5;y8B}*YomhrQUl4Zh33fZ&o%D!YgWO-yNOR|<_MwZAv^b|$1 z4p~c&WULXdElUw2YK+JGyyv{{b58%i=iKif_w&a+_k8a?_p;VlYY+$odLZC$akyT0 z4Y(Fa^b8>$pwM`R4<`hI06-Fa59C5}$RPnZAPYSN00;=21pnMR(%M&}Km!(L7TSc* z=$^fFKa5!fuSahq`Sr?``myydFdA9qeJmmU%K3y1N=h%0vMB{%-vm;*#xs}G*+Y{B z1BAKeQf>IA?PRq(J09eo^7pmNV*73U_o12+&ZolmB6Odb@>Fv{X(@(B&7|!Q&6~bh zU|rD7uu*l}%BNB0Av&LZ-NA^wVP{Q~p-e|fe2mM?9(V)l-=NHZtmjlR;JnoZbH^6;I2x zuKt3aE+TJ;G@`t`#4Z| zV(PfByq(zoF>?>64;uFPiCYl_4(UEV+D-vu&?Jv`z6`JIaJVF^`$I8JT_~CR`ybak zs|<83L@1XJ|Mr!_Q3QAL3|g?ZRM@|pu~QU#U*3Mh?)hN;38dZdz9M3$N&-hCbUC!W zy6W}rjrOyXh{!1Y!q5#`l;yjxpd_($j6&bFalp7%>|w*rD8?u$Dh& zhnSbYn_C9^KZI4oO|C@uU;DM zeKFHuyIJkq3qz`NinbLonP@6!SM5oD^KM7--m1mebq!*|XLSFzd1@6ByH*gJvOXod zH(Fa0OMrFT9^zUL3W;j}s@jGgH(}Ld$d13z1c5n|g z#CZC9>E{QEdUS@gVr4$eW#)5!Fvef**m+kO1IeTQTkGnF*;IXR#@P^h>uB1?T8f9l zr7nuXJP=c;1XRnGLp;L^qGVuR?(%y3 zBiXKS^_-a7&Y+XIiSqrZG|eVmJ@Yauyy9v<2k%l1o2_D#SyFQ9DZwvLIi{<~&y2e| z*c&;Uk#49GV{c=HXX)`R1nH?@$Cv3G?s`F>?XWK2phUwn8+Z>HZX#f`9DGwFBcuQa z6Ksuo$<(Ph+Z!V9>&Y%Um2N{yOPy5o9}FXPf`6hU9@10%d#=vbCzn<`|9u=0Hdq{} z4uQa8KoTqxBthK^VNd`Hg@=Q+&L9TKE*p0t?mGqm&0z-(P=n;%EsHAidXK+eN>UKvkDxea0 zp#P-*_kKu#_&+T_vtC@ko6OJq($nC3Il0R9b)`T|= z(TxpI;UvM~h;ya`j%K$|DveUkqwOgxNgW`~(B`skW_svLblMZd>t(7~!=6F@g`yRh zy~z;$fyd*DmFg$PXLh0{VrXUhDcn#P00D}{z~i9E!7N3%;D_P-?KS)7I!>)kVlVg? zCil$0Y`yt5{huZt?A3vZ07N{{S9^WVja*wIE@lvVMTo9J=QQJLe0*qjRXJ9<+`E4);>i4nt=K)EXsJ@Ens#;)OFz`Z~h}JB90br*0qe?J@UWS#m<`(ucTBh(~WY zT&qusDCN_NPebRa37L7#jufzt-IZ~Y0Fs64s48}{7E*XyrWW9KvG9JHWT9Luz zG78>gPEO>WFbehVA~h|BOCn7Dn0iJ@Vu^MxM|7z9?a7_m(|c`x0-q`#d>rb?oD+4F zL~7~s-r&?vRV+qho|h!e+L~La@@Qp~zfF1($h`xXcX)*5h6zn^@?GOf`Rr7t&~Bju mlH*f^c*Ep6%5+rI!$=hPYi literal 0 HcmV?d00001 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9aa3867..5a6cb9d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,8 @@ micronaut.application.name=demo -adb.hosts=127.0.0.1:8529 +adb.hosts=localhost:8529 adb.password=test +adb.useSsl=true +adb.ssl.trustStoreFile=META-INF/resources/adb.truststore +adb.ssl.trustStorePassword=12345678 +adb.ssl.trustStoreType=pkcs12 From aaa1be20eb73f7c0b7370e53584beee5e40d5aa9 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Jun 2024 16:46:55 +0200 Subject: [PATCH 3/6] added snapshot repository --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index aef5b64..fa2e6e3 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,16 @@ central https://repo.maven.apache.org/maven2 + + arangodb-snapshots + https://oss.sonatype.org/content/groups/staging + + true + + + false + + From 01d1725b0e6648775d472ae79ee514cf8081ca00 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 6 Jun 2024 10:44:29 +0200 Subject: [PATCH 4/6] updated native image configuration --- pom.xml | 28 + .../com/example/demo/native-image.properties | 46 +- .../java/graal/BrotliSubstitutions.java | 20 + .../java/graal/graal/JdkSubstitutions.java | 98 +++ .../java/graal/graal/VertxSubstitutions.java | 197 ++++++ src/native/java/graal/graal/package-info.java | 4 + .../java/graal/netty/EmptyByteBufStub.java | 33 + .../HttpContentCompressorSubstitutions.java | 68 ++ .../graal/netty/graal/NettySubstitutions.java | 606 ++++++++++++++++++ .../graal/netty/graal/ZLibSubstitutions.java | 66 ++ src/native/java/graal/netty/package-info.java | 4 + 11 files changed, 1166 insertions(+), 4 deletions(-) create mode 100644 src/native/java/graal/BrotliSubstitutions.java create mode 100644 src/native/java/graal/graal/JdkSubstitutions.java create mode 100644 src/native/java/graal/graal/VertxSubstitutions.java create mode 100644 src/native/java/graal/graal/package-info.java create mode 100644 src/native/java/graal/netty/EmptyByteBufStub.java create mode 100644 src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java create mode 100644 src/native/java/graal/netty/graal/NettySubstitutions.java create mode 100644 src/native/java/graal/netty/graal/ZLibSubstitutions.java create mode 100644 src/native/java/graal/netty/package-info.java diff --git a/pom.xml b/pom.xml index fa2e6e3..00cba70 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ com.example.ApplicationKt 1.9.23 7.7.0-SNAPSHOT + 24.0.1 @@ -316,7 +317,34 @@ arangodb-java-driver ${adb.version} + + org.graalvm.sdk + graal-sdk + 24.0.1 + provided + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + generate-sources + + add-source + + + + ${project.basedir}/src/native/java + + + + + + + shaded diff --git a/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties index c495ad1..fde6afe 100644 --- a/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties +++ b/src/main/resources/META-INF/native-image/com/example/demo/native-image.properties @@ -1,5 +1,43 @@ Args=\ ---initialize-at-build-time=\ - com.example,\ - ch.qos.logback,\ - org.slf4j + -Dio.netty.noUnsafe=true \ + -Dio.netty.leakDetection.level=DISABLED \ + --initialize-at-build-time=\ + com.example,\ + ch.qos.logback,\ + org.slf4j,\ + io.netty \ + --initialize-at-run-time=\ + io.netty.buffer.PooledByteBufAllocator,\ + io.netty.buffer.ByteBufAllocator,\ + io.netty.buffer.ByteBufUtil,\ + io.netty.buffer.AbstractReferenceCountedByteBuf,\ + io.netty.handler.ssl.JdkSslServerContext,\ + io.netty.handler.codec.compression.BrotliDecoder,\ + io.netty.handler.codec.compression.ZstdConstants,\ + io.netty.handler.codec.http2.Http2CodecUtil,\ + io.netty.handler.codec.http2.Http2ClientUpgradeCodec,\ + io.netty.handler.codec.http2.Http2ConnectionHandler,\ + io.netty.handler.codec.http2.DefaultHttp2FrameWriter,\ + io.netty.handler.codec.http.HttpObjectEncoder,\ + io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,\ + io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder,\ + io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler,\ + io.netty.handler.codec.http2.Http2ServerUpgradeCodec,\ + io.netty.handler.pcap.PcapWriteHandler$WildcardAddressHolder,\ + io.netty.util.AbstractReferenceCounted,\ + io.netty.util.concurrent.GlobalEventExecutor,\ + io.netty.util.concurrent.ImmediateEventExecutor,\ + io.netty.util.concurrent.ScheduledFutureTask,\ + io.netty.util.internal.ThreadLocalRandom,\ + io.netty.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder,\ + io.netty.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder,\ + io.netty.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder,\ + io.netty.util.NetUtilSubstitutions$NetUtilNetworkInterfacesLazyHolder,\ + io.netty.handler.ssl.util.ThreadLocalInsecureRandom,\ + io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider,\ + io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder,\ + io.netty.resolver.dns.DnsNameResolver,\ + io.netty.resolver.HostsFileEntriesResolver,\ + io.netty.resolver.dns.ResolvConf$ResolvConfLazy,\ + io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider,\ + io.vertx.core.buffer.impl.VertxByteBufAllocator diff --git a/src/native/java/graal/BrotliSubstitutions.java b/src/native/java/graal/BrotliSubstitutions.java new file mode 100644 index 0000000..ccd245c --- /dev/null +++ b/src/native/java/graal/BrotliSubstitutions.java @@ -0,0 +1,20 @@ +package graal; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +public class BrotliSubstitutions { + + @TargetClass(className = "io.netty.handler.codec.compression.Brotli") + static final class Target_io_netty_handler_codec_compression_Brotli { + @Substitute + public static boolean isAvailable() { + return false; + } + + @Substitute + public static void ensureAvailability() throws Throwable { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/native/java/graal/graal/JdkSubstitutions.java b/src/native/java/graal/graal/JdkSubstitutions.java new file mode 100644 index 0000000..049d946 --- /dev/null +++ b/src/native/java/graal/graal/JdkSubstitutions.java @@ -0,0 +1,98 @@ +package graal.graal; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import java.io.IOException; +import java.net.URL; +import java.nio.channels.spi.AsynchronousChannelProvider; + +@TargetClass(className = "jdk.internal.loader.URLClassPath$Loader") +final class Target_URLClassPath$Loader { + + @Alias + public Target_URLClassPath$Loader(URL url) { + } +} + +@TargetClass(className = "jdk.internal.loader.URLClassPath$FileLoader") +final class Target_URLClassPath$FileLoader { + + @Alias + public Target_URLClassPath$FileLoader(URL url) throws IOException { + } +} + +@TargetClass(className = "jdk.internal.loader.URLClassPath") +final class Target_jdk_internal_loader_URLClassPath { + + @Substitute + private Target_URLClassPath$Loader getLoader(final URL url) throws IOException { + String file = url.getFile(); + if (file != null && file.endsWith("/")) { + if ("file".equals(url.getProtocol())) { + return (Target_URLClassPath$Loader) (Object) new Target_URLClassPath$FileLoader( + url); + } else { + return new Target_URLClassPath$Loader(url); + } + } else { + // that must be wrong, but JarLoader is deleted by SVM + return (Target_URLClassPath$Loader) (Object) new Target_URLClassPath$FileLoader( + url); + } + } + +} + +@Substitute +@TargetClass(className = "sun.nio.ch.WindowsAsynchronousFileChannelImpl", innerClass = "DefaultIocpHolder") +@Platforms({ Platform.WINDOWS.class }) +final class Target_sun_nio_ch_WindowsAsynchronousFileChannelImpl_DefaultIocpHolder { + + @Alias + @InjectAccessors(DefaultIocpAccessor.class) + static Target_sun_nio_ch_Iocp defaultIocp; +} + +@TargetClass(className = "sun.nio.ch.Iocp") +@Platforms({ Platform.WINDOWS.class }) +final class Target_sun_nio_ch_Iocp { + + @Alias + Target_sun_nio_ch_Iocp(AsynchronousChannelProvider provider, Target_sun_nio_ch_ThreadPool pool) throws IOException { + } + + @Alias + Target_sun_nio_ch_Iocp start() { + return null; + } +} + +@TargetClass(className = "sun.nio.ch.ThreadPool") +@Platforms({ Platform.WINDOWS.class }) +final class Target_sun_nio_ch_ThreadPool { + + @Alias + static Target_sun_nio_ch_ThreadPool createDefault() { + return null; + } +} + +final class DefaultIocpAccessor { + static Target_sun_nio_ch_Iocp get() { + try { + return new Target_sun_nio_ch_Iocp(null, Target_sun_nio_ch_ThreadPool.createDefault()).start(); + } catch (IOException ioe) { + throw new InternalError(ioe); + } + } +} + +class JdkSubstitutions { + +} diff --git a/src/native/java/graal/graal/VertxSubstitutions.java b/src/native/java/graal/graal/VertxSubstitutions.java new file mode 100644 index 0000000..6419ccc --- /dev/null +++ b/src/native/java/graal/graal/VertxSubstitutions.java @@ -0,0 +1,197 @@ +package graal.graal; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import io.netty.handler.ssl.*; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.dns.AddressResolverOptions; +import io.vertx.core.eventbus.EventBusOptions; +import io.vertx.core.eventbus.impl.HandlerHolder; +import io.vertx.core.eventbus.impl.HandlerRegistration; +import io.vertx.core.eventbus.impl.MessageImpl; +import io.vertx.core.eventbus.impl.OutboundDeliveryContext; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.VertxInternal; +import io.vertx.core.impl.resolver.DefaultResolverProvider; +import io.vertx.core.impl.transports.JDKTransport; +import io.vertx.core.net.NetServerOptions; +import io.vertx.core.spi.resolver.ResolverProvider; +import io.vertx.core.spi.transport.Transport; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManagerFactory; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +@TargetClass(className = "io.vertx.core.impl.VertxBuilder") +final class Target_io_vertx_core_impl_VertxBuilder { + @Substitute + public static Transport nativeTransport() { + return JDKTransport.INSTANCE; + } +} + +/** + * This substitution forces the usage of the blocking DNS resolver + */ +@TargetClass(className = "io.vertx.core.spi.resolver.ResolverProvider") +final class TargetResolverProvider { + + @Substitute + public static ResolverProvider factory(Vertx vertx, AddressResolverOptions options) { + return new DefaultResolverProvider(); + } +} + +@TargetClass(className = "io.vertx.core.net.OpenSSLEngineOptions") +final class Target_io_vertx_core_net_OpenSSLEngineOptions { + + @Substitute + public static boolean isAvailable() { + return false; + } + + @Substitute + public static boolean isAlpnAvailable() { + return false; + } +} + +@SuppressWarnings("rawtypes") +@TargetClass(className = "io.vertx.core.eventbus.impl.clustered.ClusteredEventBus") +final class Target_io_vertx_core_eventbus_impl_clustered_ClusteredEventBusClusteredEventBus { + + @Substitute + private NetServerOptions getServerOptions() { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + public void start(Promise promise) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + public void close(Promise promise) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + public MessageImpl createMessage(boolean send, boolean isLocal, String address, MultiMap headers, Object body, + String codecName) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected void onLocalRegistration(HandlerHolder handlerHolder, Promise promise) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected HandlerHolder createHandlerHolder(HandlerRegistration registration, boolean replyHandler, + boolean localOnly, ContextInternal context) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected void onLocalUnregistration(HandlerHolder handlerHolder, Promise completionHandler) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected void sendOrPub(OutboundDeliveryContext sendContext) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected String generateReplyAddress() { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + protected boolean isMessageLocal(MessageImpl msg) { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + ConcurrentMap connections() { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + VertxInternal vertx() { + throw new RuntimeException("Not Implemented"); + } + + @Substitute + EventBusOptions options() { + throw new RuntimeException("Not Implemented"); + } +} + +@TargetClass(className = "io.vertx.core.spi.tls.DefaultSslContextFactory") +final class Target_DefaultSslContextFactory { + + @Alias + private Set enabledCipherSuites; + + @Alias + private List applicationProtocols; + + @Alias + private ClientAuth clientAuth; + + @Substitute + private SslContext createContext(boolean useAlpn, boolean client, KeyManagerFactory kmf, TrustManagerFactory tmf) + throws SSLException { + SslContextBuilder builder; + if (client) { + builder = SslContextBuilder.forClient(); + if (kmf != null) { + builder.keyManager(kmf); + } + } else { + builder = SslContextBuilder.forServer(kmf); + } + Collection cipherSuites = enabledCipherSuites; + builder.sslProvider(SslProvider.JDK); + if (cipherSuites == null || cipherSuites.isEmpty()) { + cipherSuites = Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite.get(); + } + if (tmf != null) { + builder.trustManager(tmf); + } + if (cipherSuites != null && cipherSuites.size() > 0) { + builder.ciphers(cipherSuites); + } + if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) { + builder.applicationProtocolConfig(new ApplicationProtocolConfig( + ApplicationProtocolConfig.Protocol.ALPN, + ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, + ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, + applicationProtocols)); + } + if (clientAuth != null) { + builder.clientAuth(clientAuth); + } + return builder.build(); + } +} + +@TargetClass(className = "io.vertx.core.spi.tls.DefaultJDKCipherSuite") +final class Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite { + @Alias + static List get() { + return null; + } +} + +class VertxSubstitutions { + +} diff --git a/src/native/java/graal/graal/package-info.java b/src/native/java/graal/graal/package-info.java new file mode 100644 index 0000000..794dbcf --- /dev/null +++ b/src/native/java/graal/graal/package-info.java @@ -0,0 +1,4 @@ +/** + * from io.quarkus:quarkus-vertx:3.10.1 + */ +package graal.graal; diff --git a/src/native/java/graal/netty/EmptyByteBufStub.java b/src/native/java/graal/netty/EmptyByteBufStub.java new file mode 100644 index 0000000..1dc6dab --- /dev/null +++ b/src/native/java/graal/netty/EmptyByteBufStub.java @@ -0,0 +1,33 @@ +package graal.netty; + +import io.netty.util.internal.PlatformDependent; + +import java.nio.ByteBuffer; + +public final class EmptyByteBufStub { + private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocateDirect(0); + private static final long EMPTY_BYTE_BUFFER_ADDRESS; + + static { + long emptyByteBufferAddress = 0; + try { + if (PlatformDependent.hasUnsafe()) { + emptyByteBufferAddress = PlatformDependent.directBufferAddress(EMPTY_BYTE_BUFFER); + } + } catch (Throwable t) { + // Ignore + } + EMPTY_BYTE_BUFFER_ADDRESS = emptyByteBufferAddress; + } + + public static ByteBuffer emptyByteBuffer() { + return EMPTY_BYTE_BUFFER; + } + + public static long emptyByteBufferAddress() { + return EMPTY_BYTE_BUFFER_ADDRESS; + } + + private EmptyByteBufStub() { + } +} diff --git a/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java b/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java new file mode 100644 index 0000000..2489e97 --- /dev/null +++ b/src/native/java/graal/netty/graal/HttpContentCompressorSubstitutions.java @@ -0,0 +1,68 @@ +package graal.netty.graal; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; + +import java.util.function.BooleanSupplier; + +public class HttpContentCompressorSubstitutions { + + @TargetClass(className = "io.netty.handler.codec.compression.ZstdEncoder", onlyWith = IsZstdAbsent.class) + public static final class ZstdEncoderFactorySubstitution { + + @Substitute + protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { + throw new UnsupportedOperationException(); + } + + @Substitute + protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { + throw new UnsupportedOperationException(); + } + + @Substitute + public void flush(final ChannelHandlerContext ctx) { + throw new UnsupportedOperationException(); + } + } + + @Substitute + @TargetClass(className = "io.netty.handler.codec.compression.ZstdConstants", onlyWith = IsZstdAbsent.class) + public static final class ZstdConstants { + + // The constants make calls to com.github.luben.zstd.Zstd so we cut links with that substitution. + + static final int DEFAULT_COMPRESSION_LEVEL = 0; + + static final int MIN_COMPRESSION_LEVEL = 0; + + static final int MAX_COMPRESSION_LEVEL = 0; + + static final int MAX_BLOCK_SIZE = 0; + + static final int DEFAULT_BLOCK_SIZE = 0; + } + + public static class IsZstdAbsent implements BooleanSupplier { + + private boolean zstdAbsent; + + public IsZstdAbsent() { + try { + Class.forName("com.github.luben.zstd.Zstd"); + zstdAbsent = false; + } catch (Exception e) { + // It can be a classloading issue (the library is not available), or a native issue + // (the library for the current OS/arch is not available) + zstdAbsent = true; + } + } + + @Override + public boolean getAsBoolean() { + return zstdAbsent; + } + } +} diff --git a/src/native/java/graal/netty/graal/NettySubstitutions.java b/src/native/java/graal/netty/graal/NettySubstitutions.java new file mode 100644 index 0000000..fb9b8b1 --- /dev/null +++ b/src/native/java/graal/netty/graal/NettySubstitutions.java @@ -0,0 +1,606 @@ +package graal.netty.graal; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import graal.netty.EmptyByteBufStub; +import io.netty.bootstrap.AbstractBootstrapConfig; +import io.netty.bootstrap.ChannelFactory; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.*; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.compression.ZlibCodecFactory; +import io.netty.handler.codec.compression.ZlibWrapper; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http2.Http2Exception; +import io.netty.handler.ssl.*; +import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.JdkLoggerFactory; + +import javax.crypto.NoSuchPaddingException; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.security.*; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.util.*; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.function.BooleanSupplier; + +import static io.netty.handler.codec.http.HttpHeaderValues.*; + +/** + * This substitution avoid having loggers added to the build + */ +@TargetClass(className = "io.netty.util.internal.logging.InternalLoggerFactory") +final class Target_io_netty_util_internal_logging_InternalLoggerFactory { + + @Substitute + private static InternalLoggerFactory newDefaultFactory(String name) { + return JdkLoggerFactory.INSTANCE; + } +} + +// SSL +// This whole section is mostly about removing static analysis references to openssl/tcnative + +@TargetClass(className = "io.netty.handler.ssl.SslProvider") +final class Target_io_netty_handler_ssl_SslProvider { + @Substitute + public static boolean isAlpnSupported(final SslProvider provider) { + switch (provider) { + case JDK: + return Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator.isAlpnSupported(); + case OPENSSL: + case OPENSSL_REFCNT: + return false; + default: + throw new Error("SslProvider unsupported on Quarkus " + provider); + } + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator") +final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator { + @Alias + static boolean isAlpnSupported() { + return true; + } +} + +/** + * Hardcode io.netty.handler.ssl.OpenSsl as non-available + */ +@TargetClass(className = "io.netty.handler.ssl.OpenSsl") +final class Target_io_netty_handler_ssl_OpenSsl { + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static Throwable UNAVAILABILITY_CAUSE = new RuntimeException("OpenSsl unsupported on Quarkus"); + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + static List DEFAULT_CIPHERS = Collections.emptyList(); + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + static Set AVAILABLE_CIPHER_SUITES = Collections.emptySet(); + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static Set AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet(); + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static Set AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet(); + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static boolean SUPPORTS_KEYMANAGER_FACTORY = false; + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static boolean SUPPORTS_OCSP = false; + + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + static Set SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); + + @Substitute + public static boolean isAvailable() { + return false; + } + + @Substitute + public static int version() { + return -1; + } + + @Substitute + public static String versionString() { + return null; + } + + @Substitute + public static boolean isCipherSuiteAvailable(String cipherSuite) { + return false; + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkSslServerContext") +final class Target_io_netty_handler_ssl_JdkSslServerContext { + + @Alias + Target_io_netty_handler_ssl_JdkSslServerContext(Provider provider, + X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, + ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout, + ClientAuth clientAuth, String[] protocols, boolean startTls, + String keyStore) + throws SSLException { + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkSslClientContext") +final class Target_io_netty_handler_ssl_JdkSslClientContext { + + @Alias + Target_io_netty_handler_ssl_JdkSslClientContext(Provider sslContextProvider, X509Certificate[] trustCertCollection, + TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, + String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, + CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, + long sessionCacheSize, long sessionTimeout, String keyStoreType) + throws SSLException { + + } +} + +@TargetClass(className = "io.netty.handler.ssl.SslHandler$SslEngineType") +final class Target_io_netty_handler_ssl_SslHandler$SslEngineType { + + @Alias + public static Target_io_netty_handler_ssl_SslHandler$SslEngineType JDK; + + @Substitute + static Target_io_netty_handler_ssl_SslHandler$SslEngineType forEngine(SSLEngine engine) { + return JDK; + } +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnApplicationProtocolNegotiator$AlpnWrapper") +final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator_AlpnWrapper { + @Substitute + public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, + JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { + return (SSLEngine) (Object) new Target_io_netty_handler_ssl_JdkAlpnSslEngine(engine, applicationNegotiator, + isServer); + } + +} + +@TargetClass(className = "io.netty.handler.ssl.JdkAlpnSslEngine") +final class Target_io_netty_handler_ssl_JdkAlpnSslEngine { + @Alias + Target_io_netty_handler_ssl_JdkAlpnSslEngine(final SSLEngine engine, + final JdkApplicationProtocolNegotiator applicationNegotiator, final boolean isServer) { + + } +} + +@TargetClass(className = "io.netty.handler.ssl.SslContext") +final class Target_io_netty_handler_ssl_SslContext { + + @Substitute + static SslContext newServerContextInternal(SslProvider provider, Provider sslContextProvider, + X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, + PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, + CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout, + ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, String keyStoreType, + Map.Entry, Object>... ctxOptions) throws SSLException { + if (enableOcsp) { + throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider); + } + return (SslContext) (Object) new Target_io_netty_handler_ssl_JdkSslServerContext(sslContextProvider, + trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, + keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, + clientAuth, protocols, startTls, keyStoreType); + } + + @Substitute + static SslContext newClientContextInternal(SslProvider provider, Provider sslContextProvider, + X509Certificate[] trustCert, + TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, + ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout, + boolean enableOcsp, + String keyStoreType, Map.Entry, Object>... options) throws SSLException { + if (enableOcsp) { + throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider); + } + return (SslContext) (Object) new Target_io_netty_handler_ssl_JdkSslClientContext(sslContextProvider, + trustCert, trustManagerFactory, keyCertChain, key, keyPassword, + keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, + sessionTimeout, keyStoreType); + } + +} + +@TargetClass(className = "io.netty.handler.ssl.JdkDefaultApplicationProtocolNegotiator") +final class Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator { + + @Alias + public static Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator INSTANCE; +} + +@TargetClass(className = "io.netty.handler.ssl.JdkSslContext") +final class Target_io_netty_handler_ssl_JdkSslContext { + + @Substitute + static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) { + if (config == null) { + return (JdkApplicationProtocolNegotiator) (Object) Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator.INSTANCE; + } + + switch (config.protocol()) { + case NONE: + return (JdkApplicationProtocolNegotiator) (Object) Target_io_netty_handler_ssl_JdkDefaultApplicationProtocolNegotiator.INSTANCE; + case ALPN: + if (isServer) { + // GRAAL RC9 bug: https://github.com/oracle/graal/issues/813 + // switch(config.selectorFailureBehavior()) { + // case FATAL_ALERT: + // return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); + // case NO_ADVERTISE: + // return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); + // default: + // throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") + // .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); + // } + SelectorFailureBehavior behavior = config.selectorFailureBehavior(); + if (behavior == SelectorFailureBehavior.FATAL_ALERT) { + return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); + } else if (behavior == SelectorFailureBehavior.NO_ADVERTISE) { + return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); + } else { + throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") + .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); + } + } else { + switch (config.selectedListenerFailureBehavior()) { + case ACCEPT: + return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); + case FATAL_ALERT: + return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); + default: + throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") + .append(config.selectedListenerFailureBehavior()).append(" failure behavior") + .toString()); + } + } + default: + throw new UnsupportedOperationException( + new StringBuilder("JDK provider does not support ").append(config.protocol()) + .append(" protocol") + .toString()); + } + } + +} + +/* + * This one only prints exceptions otherwise we get a useless bogus + * exception message: https://github.com/eclipse-vertx/vert.x/issues/1657 + */ +@TargetClass(className = "io.netty.bootstrap.AbstractBootstrap") +final class Target_io_netty_bootstrap_AbstractBootstrap { + + @Alias + private ChannelFactory channelFactory; + + @Alias + void init(Channel channel) throws Exception { + } + + @Alias + public AbstractBootstrapConfig config() { + return null; + } + + @Substitute + final ChannelFuture initAndRegister() { + Channel channel = null; + try { + channel = channelFactory.newChannel(); + init(channel); + } catch (Throwable t) { + // THE FIX IS HERE: + t.printStackTrace(); + if (channel != null) { + // channel can be null if newChannel crashed (eg SocketException("too many open files")) + channel.unsafe().closeForcibly(); + } + // as the Channel is not registered yet, we need to force the usage of the GlobalEventExecutor + return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t); + } + + ChannelFuture regFuture = config().group().register(channel); + if (regFuture.cause() != null) { + if (channel.isRegistered()) { + channel.close(); + } else { + channel.unsafe().closeForcibly(); + } + } + + // If we are here and the promise is not failed, it's one of the following cases: + // 1) If we attempted registration from the event loop, the registration has been completed at this point. + // i.e. It's safe to attempt bind() or connect() now because the channel has been registered. + // 2) If we attempted registration from the other thread, the registration request has been successfully + // added to the event loop's task queue for later execution. + // i.e. It's safe to attempt bind() or connect() now: + // because bind() or connect() will be executed *after* the scheduled registration task is executed + // because register(), bind(), and connect() are all bound to the same thread. + + return regFuture; + + } +} + +@TargetClass(className = "io.netty.channel.nio.NioEventLoop") +final class Target_io_netty_channel_nio_NioEventLoop { + + @Substitute + private static Queue newTaskQueue0(int maxPendingTasks) { + return new LinkedBlockingDeque<>(); + } +} + +@TargetClass(className = "io.netty.buffer.AbstractReferenceCountedByteBuf") +final class Target_io_netty_buffer_AbstractReferenceCountedByteBuf { + + @Alias + @RecomputeFieldValue(kind = Kind.FieldOffset, name = "refCnt") + private static long REFCNT_FIELD_OFFSET; +} + +@TargetClass(className = "io.netty.util.AbstractReferenceCounted") +final class Target_io_netty_util_AbstractReferenceCounted { + + @Alias + @RecomputeFieldValue(kind = Kind.FieldOffset, name = "refCnt") + private static long REFCNT_FIELD_OFFSET; +} + +// This class is runtime-initialized by NettyProcessor +final class Holder_io_netty_util_concurrent_ScheduledFutureTask { + static final long START_TIME = System.nanoTime(); +} + +@TargetClass(className = "io.netty.util.concurrent.AbstractScheduledEventExecutor") +final class Target_io_netty_util_concurrent_AbstractScheduledEventExecutor { + + // The START_TIME field is kept but not used. + // All the accesses to it have been replaced with Holder_io_netty_util_concurrent_ScheduledFutureTask + + @Substitute + static long initialNanoTime() { + return Holder_io_netty_util_concurrent_ScheduledFutureTask.START_TIME; + } + + @Substitute + static long defaultCurrentTimeNanos() { + return System.nanoTime() - Holder_io_netty_util_concurrent_ScheduledFutureTask.START_TIME; + } +} + +@TargetClass(className = "io.netty.channel.ChannelHandlerMask") +final class Target_io_netty_channel_ChannelHandlerMask { + + // Netty tries to self-optimized itself, but it requires lots of reflection. We disable this behavior and avoid + // misleading DEBUG messages in the log. + @Substitute + private static boolean isSkippable(final Class handlerType, final String methodName, final Class... paramTypes) { + return false; + } +} + +@TargetClass(className = "io.netty.util.internal.NativeLibraryLoader") +final class Target_io_netty_util_internal_NativeLibraryLoader { + + // This method can trick GraalVM into thinking that Classloader#defineClass is getting called + @Substitute + static Class tryToLoadClass(final ClassLoader loader, final Class helper) + throws ClassNotFoundException { + return Class.forName(helper.getName(), false, loader); + } + +} + +@TargetClass(className = "io.netty.buffer.EmptyByteBuf") +final class Target_io_netty_buffer_EmptyByteBuf { + + @Alias + @RecomputeFieldValue(kind = Kind.Reset) + private static ByteBuffer EMPTY_BYTE_BUFFER; + + @Alias + @RecomputeFieldValue(kind = Kind.Reset) + private static long EMPTY_BYTE_BUFFER_ADDRESS; + + @Substitute + public ByteBuffer nioBuffer() { + return EmptyByteBufStub.emptyByteBuffer(); + } + + @Substitute + public ByteBuffer[] nioBuffers() { + return new ByteBuffer[] { EmptyByteBufStub.emptyByteBuffer() }; + } + + @Substitute + public ByteBuffer internalNioBuffer(int index, int length) { + return EmptyByteBufStub.emptyByteBuffer(); + } + + @Substitute + public boolean hasMemoryAddress() { + return EmptyByteBufStub.emptyByteBufferAddress() != 0; + } + + @Substitute + public long memoryAddress() { + if (hasMemoryAddress()) { + return EmptyByteBufStub.emptyByteBufferAddress(); + } else { + throw new UnsupportedOperationException(); + } + } + +} + +@TargetClass(className = "io.netty.handler.codec.http.HttpContentDecompressor") +final class Target_io_netty_handler_codec_http_HttpContentDecompressor { + + @Alias + private boolean strict; + + @Alias + protected ChannelHandlerContext ctx; + + @Substitute + protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { + if (GZIP.contentEqualsIgnoreCase(contentEncoding) || + X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), + ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); + } + if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || + X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { + final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; + // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), + ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper)); + } + + // 'identity' or unsupported + return null; + } +} + +@TargetClass(className = "io.netty.handler.codec.http2.DelegatingDecompressorFrameListener") +final class Target_io_netty_handler_codec_http2_DelegatingDecompressorFrameListener { + + @Alias + boolean strict; + + @Substitute + protected EmbeddedChannel newContentDecompressor(ChannelHandlerContext ctx, CharSequence contentEncoding) + throws Http2Exception { + if (!HttpHeaderValues.GZIP.contentEqualsIgnoreCase(contentEncoding) + && !HttpHeaderValues.X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { + if (!HttpHeaderValues.DEFLATE.contentEqualsIgnoreCase(contentEncoding) + && !HttpHeaderValues.X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { + return null; + } else { + ZlibWrapper wrapper = this.strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(), + new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(wrapper) }); + } + } else { + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(), + new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP) }); + } + } +} + +@TargetClass(className = "io.netty.handler.ssl.SslHandler") +final class Target_SslHandler { + + @Substitute + private void setOpensslEngineSocketFd(Channel c) { + // do nothing. + } +} + +@TargetClass(className = "io.netty.handler.ssl.PemReader") +final class Alias_PemReader { + + @Alias + public static ByteBuf readPrivateKey(File keyFile) { + return null; + } + + @Alias + public static ByteBuf readPrivateKey(InputStream in) throws KeyException { + return null; + } +} + +/** + * If BouncyCastle is not on the classpath, we must not try to read the PEM file using the BouncyCatle PEM reader. + */ +@TargetClass(className = "io.netty.handler.ssl.SslContext", onlyWith = IsBouncyNotThere.class) +final class Target_SslContext { + + @Substitute + protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeySpecException, + InvalidAlgorithmParameterException, + KeyException, IOException { + if (keyFile == null) { + return null; + } + + return getPrivateKeyFromByteBuffer(Alias_PemReader.readPrivateKey(keyFile), keyPassword); + } + + @Substitute + protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword) + throws NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeySpecException, + InvalidAlgorithmParameterException, + KeyException, IOException { + if (keyInputStream == null) { + return null; + } + + return getPrivateKeyFromByteBuffer(Alias_PemReader.readPrivateKey(keyInputStream), keyPassword); + } + + @Alias + private static PrivateKey getPrivateKeyFromByteBuffer(ByteBuf encodedKeyBuf, String keyPassword) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, + InvalidAlgorithmParameterException, KeyException, IOException { + return null; + } +} + +class IsBouncyNotThere implements BooleanSupplier { + + @Override + public boolean getAsBoolean() { + try { + NettySubstitutions.class.getClassLoader().loadClass("org.bouncycastle.openssl.PEMParser"); + return false; + } catch (Exception e) { + return true; + } + } +} + +class NettySubstitutions { + +} diff --git a/src/native/java/graal/netty/graal/ZLibSubstitutions.java b/src/native/java/graal/netty/graal/ZLibSubstitutions.java new file mode 100644 index 0000000..7017aaa --- /dev/null +++ b/src/native/java/graal/netty/graal/ZLibSubstitutions.java @@ -0,0 +1,66 @@ +package graal.netty.graal; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import io.netty.handler.codec.compression.*; + +/** + * This substitution avoid having jcraft zlib added to the build + */ +@TargetClass(className = "io.netty.handler.codec.compression.ZlibCodecFactory") +final class Target_io_netty_handler_codec_compression_ZlibCodecFactory { + + @Substitute + public static ZlibEncoder newZlibEncoder(int compressionLevel) { + return new JdkZlibEncoder(compressionLevel); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper) { + return new JdkZlibEncoder(wrapper); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { + return new JdkZlibEncoder(wrapper, compressionLevel); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { + return new JdkZlibEncoder(wrapper, compressionLevel); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(byte[] dictionary) { + return new JdkZlibEncoder(dictionary); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(int compressionLevel, byte[] dictionary) { + return new JdkZlibEncoder(compressionLevel, dictionary); + } + + @Substitute + public static ZlibEncoder newZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { + return new JdkZlibEncoder(compressionLevel, dictionary); + } + + @Substitute + public static ZlibDecoder newZlibDecoder() { + return new JdkZlibDecoder(); + } + + @Substitute + public static ZlibDecoder newZlibDecoder(ZlibWrapper wrapper) { + return new JdkZlibDecoder(wrapper); + } + + @Substitute + public static ZlibDecoder newZlibDecoder(byte[] dictionary) { + return new JdkZlibDecoder(dictionary); + } +} + +class ZLibSubstitutions { + +} diff --git a/src/native/java/graal/netty/package-info.java b/src/native/java/graal/netty/package-info.java new file mode 100644 index 0000000..8b55354 --- /dev/null +++ b/src/native/java/graal/netty/package-info.java @@ -0,0 +1,4 @@ +/** + * from io.quarkus:quarkus-netty:3.10.1 + */ +package graal.netty; From 5cab87fe42122bc9569acd08b0530f3c32d99c0e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 6 Jun 2024 15:39:23 +0200 Subject: [PATCH 5/6] fixed activation of shaded profile --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00cba70..0eaf5cd 100644 --- a/pom.xml +++ b/pom.xml @@ -308,7 +308,8 @@ plain - !shaded + shaded + !true @@ -351,6 +352,7 @@ shaded + true From 8daf0e3fbf74f0c02739f0fdfd68471cd87bdff0 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 7 Jun 2024 16:01:13 +0200 Subject: [PATCH 6/6] updated java driver 7.7.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0eaf5cd..23ca48b 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ netty com.example.ApplicationKt 1.9.23 - 7.7.0-SNAPSHOT + 7.7.0 24.0.1