Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Micronaut cannot load Truffle/polyglot languages within the shaded jar. #866

Open
chumer opened this issue Oct 2, 2023 · 5 comments
Open
Labels
status: under consideration The issue is being considered, but has not been accepted yet

Comments

@chumer
Copy link

chumer commented Oct 2, 2023

Expected Behavior

With GraalVM 23.1 (GraalVM for JDK 21) GraalVM Truffle/Polyglot languages are now consumed from the class/module-path.
For context, this project is also known as Truffle Unchained, and we wrote up all about it here: https://medium.com/graalvm/truffle-unchained-13887b77b62c

For that to work, we require the optimizing Truffle runtime to run as a named module. The reason is that we would otherwise need to export the internal JVMCI API to ALL-UNNAMED. The JVMCI is unsafe on steroids and must not be exported to ALL-UNNAMED to preserve the VM integrity.

There is no problem with regular Java SE applications that leverage the module-path or use a module class loader as the truffle runtime runs as named module. As far as I understand Micronaut cannot run modules as named modules atm, therefore this solution cannot be applied. But please correct me if I am wrong.

When running languages from the classpath the Polyglot runtime automatically scans the classpath and looks for modular jar files that it can spawn on a separate module-layer + classloader. This approach typically works for regular Java SE applications, but it fails when Micronaut shades everything into a single jar file.

As a workaround, if you run with mvn mn:run and it works because it uses the regular class-path.

Another workaround I had to deploy to make micronaut work with GraalVM for JDK 21 was I had to add these two dependencies:

    <dependency>
      <groupId>org.graalvm.sdk</groupId>
      <artifactId>graal-sdk</artifactId>
      <version>23.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.graalvm.truffle</groupId>
      <artifactId>truffle-api</artifactId>
      <version>23.1.0</version>
    </dependency>

The reason for this workaround is that the micronaut plugin depends on the two dependencies with version 23.0.0 which causes issues.

I've discussed this briefly with @sdelamo at the GraalVM Meetup and he asked me to open an issue to start the discussion.
I do not think shading all the language jars into a single jar can work because this makes it impossible to run jars as named modules.
Are there already plans to revisit this? Do you have alternative ways to deploy that we could document?

Other frameworks like Spring and Quarkus use custom class loaders, but they keep the jar files intact, so there in theory we enumerate jar files for the module-layer with an integration API. I do not see how this can work with shading.

Quarkus issue:
quarkusio/quarkus#36242

I've discussed this briefly with @sdelamo at the GraalVM Meetup and he asked me to open an issue to start the discussion.

Actual Behaviour

~/g/1/f/embedding-micronaut $ $JAVA_HOME/bin/java -jar ./target/micronautguide-0.1.jar
 __  __ _                                  _
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
15:28:27.082 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 359ms. Server Running: http://localhost:8080
15:28:29.766 [default-nioEventLoopGroup-1-2] ERROR i.m.http.server.RouteExecutor - Unexpected error occurred: No language and polyglot implementation was found on the class-path. Make sure at last one language is added on the class-path. If you put a language on the class-path and you encounter this error then there could be a problem with isolated class loading. Use -Dpolyglotimpl.TraceClassPathIsolation=true to debug class loader islation problems. For best performance it is recommended to use polyglot from the module-path instead of the class-path.
java.lang.IllegalStateException: No language and polyglot implementation was found on the class-path. Make sure at last one language is added on the class-path. If you put a language on the class-path and you encounter this error then there could be a problem with isolated class loading. Use -Dpolyglotimpl.TraceClassPathIsolation=true to debug class loader islation problems. For best performance it is recommended to use polyglot from the module-path instead of the class-path.
	at org.graalvm.polyglot.Engine$PolyglotInvalid.noPolyglotImplementationFound(Engine.java:2071)
	at org.graalvm.polyglot.Engine$PolyglotInvalid.createHostAccess(Engine.java:2057)
	at org.graalvm.polyglot.Engine$PolyglotInvalid.createHostAccess(Engine.java:2023)
	at org.graalvm.polyglot.Engine$Builder.build(Engine.java:753)
	at org.graalvm.polyglot.Context$Builder.build(Context.java:1925)
	at example.micronaut.Application.index(Application.java:38)
	at example.micronaut.$Application$Definition$Exec.dispatch(Unknown Source)
	at io.micronaut.context.AbstractExecutableMethodsDefinition$DispatchedExecutableMethod.invoke(AbstractExecutableMethodsDefinition.java:442)
	at io.micronaut.context.DefaultBeanContext$BeanContextExecutionHandle.invoke(DefaultBeanContext.java:4256)
	at io.micronaut.web.router.AbstractRouteMatch.execute(AbstractRouteMatch.java:223)
	at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:74)
	at io.micronaut.http.server.RouteExecutor.executeRouteAndConvertBody(RouteExecutor.java:480)
	at io.micronaut.http.server.RouteExecutor.callRoute(RouteExecutor.java:470)
	at io.micronaut.http.server.RequestLifecycle.lambda$normalFlow$2(RequestLifecycle.java:146)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.server.RequestLifecycle.lambda$normalFlow$4(RequestLifecycle.java:146)
	at io.micronaut.http.server.RequestLifecycle.lambda$runWithFilters$14(RequestLifecycle.java:264)
	at io.micronaut.http.filter.FilterRunner.processRequestFilter(FilterRunner.java:308)
	at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner.lambda$filterRequest0$3(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner.processRequestFilter(FilterRunner.java:242)
	at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner.lambda$filterRequest0$3(FilterRunner.java:183)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.filter.FilterRunner.processRequestFilter(FilterRunner.java:272)
	at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner.filterRequest(FilterRunner.java:167)
	at io.micronaut.http.filter.FilterRunner.run(FilterRunner.java:162)
	at io.micronaut.http.server.RequestLifecycle.runWithFilters(RequestLifecycle.java:281)
	at io.micronaut.http.server.RequestLifecycle.normalFlow(RequestLifecycle.java:143)
	at io.micronaut.http.server.netty.NettyRequestLifecycle.handleNormal(NettyRequestLifecycle.java:85)
	at io.micronaut.http.server.netty.RoutingInBoundHandler.accept(RoutingInBoundHandler.java:220)
	at io.micronaut.http.server.netty.handler.PipeliningServerHandler$MessageInboundHandler.read(PipeliningServerHandler.java:357)
	at io.micronaut.http.server.netty.handler.PipeliningServerHandler.channelRead(PipeliningServerHandler.java:206)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
	at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Steps To Reproduce

  1. Download and unpack: embedding-micronaut.zip
  2. Download GraalVM for JDK 21 and set your JAVA_HOME to the installation.
  3. Run mvn package in the embedding-micronaut folder
  4. Run $JAVA_HOME/bin/java -jar ./target/micronautguide-0.1.jar

Environment Information

GraalVM for JDK 21
Polyglot version 23.1.0

Example Application

No response

Version

4.0.3

@graemerocher
Copy link
Contributor

probably works if you exclude shading and/or use docker image packaging

@chumer
Copy link
Author

chumer commented Oct 3, 2023

@graemerocher can you please point me to docs I can link from our docs?

@sdelamo sdelamo added the status: under consideration The issue is being considered, but has not been accepted yet label Oct 3, 2023
@graemerocher
Copy link
Contributor

@chumer think you need to configure true for the shade plugin in maven https://maven.apache.org/plugins/maven-shade-plugin/shade-mojo.html#skip

@alvarosanchez think we need an example in the docs

@chumer
Copy link
Author

chumer commented Oct 5, 2023

Yes.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.5.1</version>
  <configuration>
    <skip>true</skip>
  </configuration>
</plugin>

skips shading. But when users are used to shading as their default deployment model, how will they deploy then? I fear a Micronaut user might get lost here. I guess we should offer an alternative way to build deployable artefacts, right? Or are users already expected to know what to do?

@alvarosanchez
Copy link
Member

The only long-term solution is to get rid of shading, it's very problematic. It's been discussed for years, but we never get to the point to put some effort on it. Perhaps it's about time.

One thing that you should be aware thought is that the Maven Shade Plugin will keep a copy of the original JAR as original-xxx. Can you not use that?

@graemerocher graemerocher transferred this issue from micronaut-projects/micronaut-core Oct 9, 2023
@sdelamo sdelamo removed this from 4.3.0 Release Jan 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: under consideration The issue is being considered, but has not been accepted yet
Projects
Development

No branches or pull requests

4 participants