getkyo / kyo

Toolkit for Scala Development
https://getkyo.io
Apache License 2.0
534 stars 44 forks source link

java.lang.NoSuchFieldException: producerIndex on GraalVM Native Image #759

Open elgca opened 1 day ago

elgca commented 1 day ago

When running application built with graalvm native-image, start fails due to java.lang.NoSuchFieldException: producerIndex

I have made some settings in the META-INF/native-image, including files created from -agentlib:native-image-agentreflect-config.json...

OS : windows 11 graalvm: graalvm-oracle:21

.\out\server\nativeImage.dest\hello.exe
Exception in thread "main" java.lang.ExceptionInInitializerError
        at java.base@21.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:604)
        at java.base@21.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:604)
        at java.base@21.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:604)
        at java.base@21.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:604)
        at kyo.kernel.TracePool$.<clinit>(TracePool.scala:12)
        at kyo.kernel.TracePool$Local.borrow(TracePool.scala:20)
        at kyo.KyoApp$.attempt(KyoApp.scala:76)
        at kyo.KyoApp$.run(KyoApp.scala:96)
        at kyo.KyoApp$.run(KyoApp.scala:114)
        at kyo.KyoApp.handle(KyoApp.scala:25)
        at kyo.KyoApp$Base.run$$anonfun$1(KyoApp.scala:53)
        at kyo.KyoApp$.kyo$KyoApp$Base$$_$main$$anonfun$1(KyoApp.scala:50)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:619)
        at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:617)
        at scala.collection.AbstractIterable.foreach(Iterable.scala:935)
        at kyo.KyoApp$Base.main(KyoApp.scala:50)
        at tech.elgca.MainApp.main(KyoDemo.scala)
        at java.base@21.0.5/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.RuntimeException: java.lang.NoSuchFieldException: producerIndex
        at org.jctools.util.UnsafeAccess.fieldOffset(UnsafeAccess.java:111)
        at org.jctools.queues.MpmcArrayQueueProducerIndexField.<clinit>(MpmcArrayQueue.java:51)
        ... 20 more
Caused by: java.lang.NoSuchFieldException: producerIndex
        at java.base@21.0.5/java.lang.Class.checkField(DynamicHub.java:1044)
        at java.base@21.0.5/java.lang.Class.getDeclaredField(DynamicHub.java:1159)
        at org.jctools.util.UnsafeAccess.fieldOffset(UnsafeAccess.java:107)
        ... 21 more

native-image options:

override def nativeImageOptions = Seq(
    "--no-fallback",
    "--enable-url-protocols=http,https",
    "-Djdk.http.auth.tunneling.disabledSchemes=",
    "--initialize-at-build-time=kyo.kernel.TracePool",
    "--initialize-at-build-time=org.slf4j",
    "--initialize-at-build-time=org.slf4j.simple.SimpleLogger",
    "--initialize-at-build-time=org.xml.sax.helpers.LocatorImpl",
    "--initialize-at-build-time=org.xml.sax.helpers.AttributesImpl",
    "--initialize-at-build-time=org.slf4j.LoggerFactory",
    "--initialize-at-run-time=io.netty.channel.DefaultFileRegion",
    "--initialize-at-run-time=org.jctools.queues.MpmcArrayQueue",
    "--initialize-at-run-time=io.netty.channel.epoll.Native",
    "--initialize-at-run-time=io.netty.channel.epoll.Epoll",
    "--initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop",
    "--initialize-at-run-time=io.netty.channel.epoll.EpollEventArray",
    "--initialize-at-run-time=io.netty.channel.kqueue.KQueue",
    "--initialize-at-run-time=io.netty.channel.kqueue.KQueueEventLoop",
    "--initialize-at-run-time=io.netty.channel.kqueue.KQueueEventArray",
    "--initialize-at-run-time=io.netty.channel.kqueue.Native",
    "--initialize-at-run-time=io.netty.channel.unix.Limits",
    "--initialize-at-run-time=io.netty.channel.unix.Errors",
    "--initialize-at-run-time=io.netty.channel.unix.IovArray",
    "--initialize-at-run-time=io.netty.handler.ssl.BouncyCastleAlpnSslUtils",
    "--initialize-at-run-time=io.netty.handler.ssl.JdkSslServerContext",
    "--initialize-at-run-time=io.netty.incubator.codec.quic.InsecureQuicTokenHandler",
    "--initialize-at-run-time=io.netty.incubator.codec.quic.SecureRandomQuicConnectionIdGenerator",
    "--initialize-at-run-time=io.netty.incubator.channel.uring.IOUringEventLoopGroup",
    "--initialize-at-run-time=io.netty.incubator.channel.DefaultFileRegion",
    "--initialize-at-run-time=io.netty.incubator.channel.uring.Native",
  ) ++ (if (sys.props.get("os.name").contains("Linux")) Seq("--static") else Seq.empty)

here is the demo:

server.zip

elgca commented 1 day ago

There are more things. Routes can only add endpoints and must include execution logic.

  def add[A: Tag, I, E: SafeClassTag, O: Flat](e: Endpoint[A, I, E, O, Any])(
        f: I => O < (Async & Env[A] & Abort[E])
    )(using Frame): Unit < Routes =

There should be support for loading existing endpoints in the library class, whether it's integrating existing endpoints or adding tools for endpoints provided by tapir such as swagger, staticResource, and staticFiles. It's very convenient. I spent a lot of time understanding the Emit, but it is not need work when creating swagger endpoints. I have to do something like :

def swaggerUI2(r: Unit < Routes): Unit < Routes = {
  Emit.run[Route].apply(r).map { (seq, _) =>
    val end: List[AnyEndpoint] = seq.toSeq.map { x =>
      val z: AnyEndpoint = x.endpoint.endpoint
      z
    }.toList
    SwaggerInterpreter()
      .fromEndpoints(end, "hello-kyo", "1.0.0")
      .map(Route(_))
      .foldLeft(r) { (a, b) =>
        a.andThen(Emit(b).unit)
      }
  }
}