You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Quarkus is not a new stuff in the Java world, nor GraalVM.
GraalVM is capable of creating native executables based on Java classes or .jar files.
Quarkus is utilizing this feature and letting you creating low footprint and fast startup time based native executables.
In the near past I was experimenting with two libraries that are using the nv-websocket-client lib for integrating WebSockets:
If any developer would like to integrate Quarkus with any of the mentioned libraries using the "Quarkus way" (you will find this in that repository) you would be able to do it, but you will not be able to build it as a native image, because the library is breaking some GraalVM based limitations.
The problem itself (what I recognized)
Prerequisites to reproduce a problem:
Java 11 on your computer
Properly setup GraalVM with GRAALVM_HOME / Docker environment (if GraalVM is not available on your computer Quarkus will use Docker instead)
Maven (or use the given Maven Wrapper)
Invoke the following command in the project directory: ./mvnw package -Pnative
After a few minutes you should see the following output:
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. Object has been initialized by the com.neovisionaries.ws.client.Misc class initializer with a trace:
at java.security.SecureRandom.<init>(SecureRandom.java:218)
at com.neovisionaries.ws.client.Misc.<clinit>(Misc.java:40)
. Try avoiding to initialize the class that caused initialization of the object. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Detailed message:
Trace:
at parsing com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
Call path from entry point to com.neovisionaries.ws.client.Misc.nextBytes(byte[]):
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:128)
at com.neovisionaries.ws.client.WebSocketOutputStream.write(WebSocketOutputStream.java:48)
at com.neovisionaries.ws.client.WritingThread.sendFrame(WritingThread.java:486)
at com.neovisionaries.ws.client.WritingThread.sendFrames(WritingThread.java:360)
at com.neovisionaries.ws.client.WritingThread.main(WritingThread.java:120)
at com.neovisionaries.ws.client.WritingThread.runMain(WritingThread.java:55)
at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
at app//com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596)
at app//com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)
com.oracle.svm.core.util.UserError$UserException: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. Object has been initialized by the com.neovisionaries.ws.client.Misc class initializer with a trace:
at java.security.SecureRandom.<init>(SecureRandom.java:218)
at com.neovisionaries.ws.client.Misc.<clinit>(Misc.java:40)
. Try avoiding to initialize the class that caused initialization of the object. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Detailed message:
Trace:
at parsing com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
Call path from entry point to com.neovisionaries.ws.client.Misc.nextBytes(byte[]):
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:128)
at com.neovisionaries.ws.client.WebSocketOutputStream.write(WebSocketOutputStream.java:48)
at com.neovisionaries.ws.client.WritingThread.sendFrame(WritingThread.java:486)
at com.neovisionaries.ws.client.WritingThread.sendFrames(WritingThread.java:360)
at com.neovisionaries.ws.client.WritingThread.main(WritingThread.java:120)
at com.neovisionaries.ws.client.WritingThread.runMain(WritingThread.java:55)
at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
at app//com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596)
at app//com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)
at com.oracle.svm.core.util.UserError.abort(UserError.java:87)
at com.oracle.svm.hosted.FallbackFeature.reportAsFallback(FallbackFeature.java:233)
at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:759)
at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:529)
at com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:488)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:403)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:569)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:122)
at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:599)
Caused by: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. Object has been initialized by the com.neovisionaries.ws.client.Misc class initializer with a trace:
at java.security.SecureRandom.<init>(SecureRandom.java:218)
at com.neovisionaries.ws.client.Misc.<clinit>(Misc.java:40)
. Try avoiding to initialize the class that caused initialization of the object. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Detailed message:
Trace:
at parsing com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
Call path from entry point to com.neovisionaries.ws.client.Misc.nextBytes(byte[]):
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:115)
at com.neovisionaries.ws.client.Misc.nextBytes(Misc.java:128)
at com.neovisionaries.ws.client.WebSocketOutputStream.write(WebSocketOutputStream.java:48)
at com.neovisionaries.ws.client.WritingThread.sendFrame(WritingThread.java:486)
at com.neovisionaries.ws.client.WritingThread.sendFrames(WritingThread.java:360)
at com.neovisionaries.ws.client.WritingThread.main(WritingThread.java:120)
GB at com.neovisionaries.ws.client.WritingThread.runMain(WritingThread.java:55)
at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
at app//com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596)
at app//com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)
at com.oracle.graal.pointsto.constraints.UnsupportedFeatures.report(UnsupportedFeatures.java:126)
at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:756)
... 6 more
Caused by: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. Object has been initialized by the com.neovisionaries.ws.client.Misc class initializer with a trace:
at java.security.SecureRandom.<init>(SecureRandom.java:218)
at com.neovisionaries.ws.client.Misc.<clinit>(Misc.java:40)
. Try avoiding to initialize the class that caused initialization of the object. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
at com.oracle.svm.hosted.image.DisallowedImageHeapObjectFeature.error(DisallowedImageHeapObjectFeature.java:173)
at com.oracle.svm.core.image.DisallowedImageHeapObjects.check(DisallowedImageHeapObjects.java:65)
at com.oracle.svm.hosted.image.DisallowedImageHeapObjectFeature.replacer(DisallowedImageHeapObjectFeature.java:149)
at com.oracle.graal.pointsto.meta.AnalysisUniverse.replaceObject(AnalysisUniverse.java:570)
at com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider.replaceObject(AnalysisConstantReflectionProvider.java:217)
at com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider.interceptValue(AnalysisConstantReflectionProvider.java:188)
at com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider.readValue(AnalysisConstantReflectionProvider.java:102)
at com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider.readFieldValue(AnalysisConstantReflectionProvider.java:81)
at jdk.internal.vm.compiler/org.graalvm.compiler.nodes.util.ConstantFoldUtil$1.readValue(ConstantFoldUtil.java:51)
at jdk.internal.vm.compiler/org.graalvm.compiler.core.common.spi.JavaConstantFieldProvider.readConstantField(JavaConstantFieldProvider.java:84)
at com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider.readConstantField(AnalysisConstantFieldProvider.java:72)
at jdk.internal.vm.compiler/org.graalvm.compiler.nodes.util.ConstantFoldUtil.tryConstantFold(ConstantFoldUtil.java:47)
at com.oracle.svm.hosted.phases.ConstantFoldLoadFieldPlugin.tryConstantFold(ConstantFoldLoadFieldPlugin.java:61)
at com.oracle.svm.hosted.phases.ConstantFoldLoadFieldPlugin.handleLoadStaticField(ConstantFoldLoadFieldPlugin.java:57)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genGetStatic(BytecodeParser.java:4944)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genGetStatic(BytecodeParser.java:4911)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5413)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3477)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.handleBytecodeBlock(BytecodeParser.java:3437)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3282)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1145)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:1030)
at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase.run(SharedGraphBuilderPhase.java:81)
at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:212)
at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
at com.oracle.graal.pointsto.flow.AnalysisParsedGraph.parseBytecode(AnalysisParsedGraph.java:132)
at com.oracle.graal.pointsto.meta.AnalysisMethod.ensureGraphParsed(AnalysisMethod.java:621)
at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:163)
at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:321)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.createTypeFlow(MethodTypeFlow.java:293)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureTypeFlowCreated(MethodTypeFlow.java:282)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:103)
at com.oracle.graal.pointsto.flow.StaticInvokeTypeFlow.update(InvokeTypeFlow.java:420)
at com.oracle.graal.pointsto.PointsToAnalysis$2.run(PointsToAnalysis.java:595)
at com.oracle.graal.pointsto.util.CompletionExecutor.executeCommand(CompletionExecutor.java:188)
at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$executeService$0(CompletionExecutor.java:172)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool.awaitQuiescence(ForkJoinPool.java:2984)
at com.oracle.graal.pointsto.util.CompletionExecutor.complete(CompletionExecutor.java:238)
at com.oracle.graal.pointsto.PointsToAnalysis.checkObjectGraph(PointsToAnalysis.java:680)
at com.oracle.graal.pointsto.PointsToAnalysis.finish(PointsToAnalysis.java:644)
at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:704)
... 6 more
Error: Image build request failed with exit status 1
The most interesting stuff is this:
Caused by: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. Object has been initialized by the com.neovisionaries.ws.client.Misc class initializer with a trace:
at java.security.SecureRandom.<init>(SecureRandom.java:218)
at com.neovisionaries.ws.client.Misc.<clinit>(Misc.java:40)
The com.neovisionaries.ws.client.Misc class is initiating (com.neovisionaries.ws.client.Misc.sRandom) an instance of the java.security.SecureRandom class and because of that GraalVM native image build fails, this is a limitation of GraalVM. That during build time this can not happen (https://www.graalvm.org/reference-manual/native-image/JCASecurityServices/), so any the following flag for the compiler will not make any difference in the result, only in the error message: --initialize-at-run-time=com.neovisionaries.ws.client.Misc.
Quarkus is basically, because its augmentation process, initiating a lot of things at build time, trying to enhance most of the code and at native image build phase the whole process fails because the connection is being setup and it breaks. I think the whole flow can be visualized with the following call chain:
I do not really have a proper vision why in the com.neovisionaries.ws.client.Misc class the sRandom field is initialized there (but I think it is a good way for SecureRandom), it is being used only one place in the com.neovisionaries.ws.client.Misc.nextBytes(byte[]) method, but a possible fix would just change this SecureRandom initiation and would be placed in the method itself. So:
classMisc {
//private static final SecureRandom sRandom = new SecureRandom();
...
/** * Fill the given buffer with random bytes. */publicstaticbyte[] nextBytes(byte[] buffer)
{
newSecureRandom().nextBytes(buffer);
returnbuffer;
}
...
}
Or if it is a really bad one then maybe the following:
/** * WebSocket Security. */publicfinalclassSecurity {
privatefinalSecureRandomsRandom;
privatestaticSecurityinstance;
privateSecurity() {
sRandom = newSecureRandom();
}
/** * Returns the security instance. * * @return security instance. */publicstaticSecuritygetInstance() {
if (instance == null) {
instance = newSecurity();
}
returninstance;
}
/** * Fill the given buffer with random bytes. */publicbyte[] nextBytes(byte[] buffer) {
sRandom.nextBytes(buffer);
returnbuffer;
}
/** * Create a buffer of the given size filled with random bytes. */publicbyte[] nextBytes(intnBytes) {
byte[] buffer = newbyte[nBytes];
returnnextBytes(buffer);
}
}
With these changes I think that the functionality does not get hurt and after these changes this project would also work in native mode too, not only this project but a project that is based on the mentioned JavaCord and JDA using the same dependencies and CDI way (@produces) to create a connection to the specific websocket server.
Motivation behind the change
I'm planning to create Quarkus extensions for these two mentioned libraries and in order to make sure they could be compiled to native binaries this change is a must right now. Of course, native image mode is not a "must have" for Quarkus extension, but the framework itself was built around utilizing the native image generation.
The text was updated successfully, but these errors were encountered:
Hey @TakahikoKawasaki ,
Sorry for disturbing you, I just would like to ask if is there any chance that would be reviewed and maybe merged in the future?
Quarkus is not a new stuff in the Java world, nor GraalVM.
GraalVM is capable of creating native executables based on Java classes or .jar files.
Quarkus is utilizing this feature and letting you creating low footprint and fast startup time based native executables.
In the near past I was experimenting with two libraries that are using the nv-websocket-client lib for integrating WebSockets:
If any developer would like to integrate Quarkus with any of the mentioned libraries using the "Quarkus way" (you will find this in that repository) you would be able to do it, but you will not be able to build it as a native image, because the library is breaking some GraalVM based limitations.
The problem itself (what I recognized)
Prerequisites to reproduce a problem:
Invoke the following command in the project directory:
./mvnw package -Pnative
After a few minutes you should see the following output:
The most interesting stuff is this:
The
com.neovisionaries.ws.client.Misc
class is initiating (com.neovisionaries.ws.client.Misc.sRandom
) an instance of thejava.security.SecureRandom
class and because of that GraalVM native image build fails, this is a limitation of GraalVM. That during build time this can not happen (https://www.graalvm.org/reference-manual/native-image/JCASecurityServices/), so any the following flag for the compiler will not make any difference in the result, only in the error message:--initialize-at-run-time=com.neovisionaries.ws.client.Misc
.Quarkus is basically, because its augmentation process, initiating a lot of things at build time, trying to enhance most of the code and at native image build phase the whole process fails because the connection is being setup and it breaks. I think the whole flow can be visualized with the following call chain:
com.neovisionaries.ws.client.WebSocket.connect()
com.neovisionaries.ws.client.WebSocket.shakeHands
com.neovisionaries.ws.client.WebSocket.generateWebSocketKey
com.neovisionaries.ws.client.Misc.nextBytes(byte[])
A possible solution
I do not really have a proper vision why in the
com.neovisionaries.ws.client.Misc
class the sRandom field is initialized there (but I think it is a good way for SecureRandom), it is being used only one place in thecom.neovisionaries.ws.client.Misc.nextBytes(byte[])
method, but a possible fix would just change this SecureRandom initiation and would be placed in the method itself. So:Or if it is a really bad one then maybe the following:
And replace the Misc references in the code:
With these changes I think that the functionality does not get hurt and after these changes this project would also work in native mode too, not only this project but a project that is based on the mentioned JavaCord and JDA using the same dependencies and CDI way (@produces) to create a connection to the specific websocket server.
Motivation behind the change
I'm planning to create Quarkus extensions for these two mentioned libraries and in order to make sure they could be compiled to native binaries this change is a must right now. Of course, native image mode is not a "must have" for Quarkus extension, but the framework itself was built around utilizing the native image generation.
The text was updated successfully, but these errors were encountered: