quarkus-cxf
quarkus-cxf copied to clipboard
Service with `@RequestWrapper` and `@ResponseWrapper` cannot be compiled to native
There is a reproducer in https://github.com/quarkiverse/quarkus-cxf/pull/579
The stack trace:
[INFO] [stdout] ========================================================================================================================
[INFO] [stdout] GraalVM Native Image: Generating 'quarkus-cxf-integration-test-mtom-awt-1.6.0-SNAPSHOT-runner' (executable)...
[INFO] [stdout] ========================================================================================================================
[INFO] [stdout] [1/7] Initializing... (7.8s @ 0.17GB)
[INFO] [stdout] Version info: 'GraalVM 22.2.0 Java 17 CE'
[INFO] [stdout] Java version info: '17.0.4+8-jvmci-22.2-b06'
[INFO] [stdout] C compiler: gcc (redhat, x86_64, 8.5.0)
[INFO] [stdout] Garbage collector: Serial GC
[INFO] [stdout] 6 user-specific feature(s)
[INFO] [stdout] - io.quarkus.awt.runtime.graal.AwtFeature
[INFO] [stdout] - io.quarkus.awt.runtime.graal.DarwinAwtFeature
[INFO] [stdout] - io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
[INFO] [stdout] - io.quarkus.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase for the [org.jboss.threads] categories
[INFO] [stdout] - io.quarkus.runtime.graal.ResourcesFeature: Register each line in META-INF/quarkus-native-resources.txt as a resource on Substrate VM
[INFO] [stdout] - org.graalvm.home.HomeFinderFeature: Finds GraalVM paths and its version number
[INFO] [stdout] [2/7] Performing analysis... [] (5.7s @ 0.83GB)
[INFO] [stdout] 5,601 (89.44%) of 6,262 classes reachable
[INFO] [stdout] 7,089 (56.75%) of 12,491 fields reachable
[INFO] [stdout] 22,941 (77.54%) of 29,585 methods reachable
[INFO] [stdout] 873 classes, 0 fields, and 0 methods registered for reflection
[INFO] [stdout] 2 native libraries: m, stdc++
[INFO] [stdout]
[WARN] [stderr] Error: Classes that should be initialized at run time got initialized during image building:
[WARN] [stderr] java.awt.Image the class was requested to be initialized at run time (Required for sun.text.bidi.BidiBase.NumericShapings). jdk.proxy4.$Proxy131 caused initialization of this class with the following trace:
[WARN] [stderr] at java.awt.Image.<clinit>(Image.java:58)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.UserError.abort(UserError.java:72)
[WARN] [stderr] at java.lang.Class.forName0(Unknown Source)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.checkDelayedInitialization(ConfigurableClassInitialization.java:554)
[WARN] [stderr] at java.lang.Class.forName(Class.java:375)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.classinitialization.ClassInitializationFeature.duringAnalysis(ClassInitializationFeature.java:172)
[WARN] [stderr] at jdk.proxy4.$Proxy131.<clinit>(Unknown Source)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.lambda$runPointsToAnalysis$10(NativeImageGenerator.java:734)
[WARN] [stderr]
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.FeatureHandler.forEachFeature(FeatureHandler.java:78)
[WARN] [stderr]
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.lambda$runPointsToAnalysis$11(NativeImageGenerator.java:734)
[WARN] [stderr] com.oracle.svm.core.util.UserError$UserException: Classes that should be initialized at run time got initialized during image building:
[WARN] [stderr] at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.PointsToAnalysis.runAnalysis(PointsToAnalysis.java:755)
[WARN] [stderr] java.awt.Image the class was requested to be initialized at run time (Required for sun.text.bidi.BidiBase.NumericShapings). jdk.proxy4.$Proxy131 caused initialization of this class with the following trace:
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:731)
[WARN] [stderr] at java.awt.Image.<clinit>(Image.java:58)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:564)
[WARN] [stderr] at java.lang.Class.forName0(Unknown Source)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:521)
[WARN] [stderr] at java.lang.Class.forName(Class.java:375)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:407)
[WARN] [stderr] at jdk.proxy4.$Proxy131.<clinit>(Unknown Source)
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:585)
[WARN] [stderr]
[WARN] [stderr] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:128)
[INFO] [stdout] ------------------------------------------------------------------------------------------------------------------------
[WARN] [stderr]
[INFO] [stdout] 0.7s (4.5% of total time) in 19 GCs | Peak RSS: 2.29GB | CPU load: 9.89
[INFO] [stdout] ========================================================================================================================
[INFO] [stdout] Failed generating 'quarkus-cxf-integration-test-mtom-awt-1.6.0-SNAPSHOT-runner' after 14.0s.
[WARN] [stderr] Error: Image build request failed with exit status 1
I wonder where that jdk.proxy4.$Proxy131 class that requests the initialization of java.awt.Image comes from? Any idea @shumonsharif ?
Hey @ppalaga Apologies for the late response, just saw this. My suggestion would be to add the following:
quarkus.native.additional-build-args = --trace-class-initialization=java.awt.Image,-J-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
That should dump all the Proxy classes under:
target/quarkus-cxf-integration-test-mtom-awt-1.6.0-SNAPSHOT-native-image-source-jar/com/sun/proxy
If you then decompile/view the relevant Proxy source, you should be able to see which interfaces it implements ... that should hopefully lead you to the culprit.
These are the interfaces I see for the error on my laptop:
public final class $Proxy317 extends Proxy implements ImageService, BindingProvider, Closeable, Client {
It looks like the ImageService implementation itself is causing your issue.
Thanks, that helped a lot! I was not aware of -J-Djdk.proxy.ProxyGenerator.saveGeneratedFiles
Here is the decompiled code:
package jdk.proxy4;
...
public final class $Proxy133 extends Proxy implements ImageService, BindingProvider, Closeable, Client {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
private static final Method m4;
private static final Method m5;
private static final Method m6;
private static final Method m7;
private static final Method m8;
private static final Method m9;
private static final Method m10;
private static final Method m11;
private static final Method m12;
private static final Method m13;
private static final Method m14;
private static final Method m15;
private static final Method m16;
private static final Method m17;
private static final Method m18;
private static final Method m19;
private static final Method m20;
private static final Method m21;
private static final Method m22;
private static final Method m23;
private static final Method m24;
private static final Method m25;
private static final Method m26;
private static final Method m27;
private static final Method m28;
private static final Method m29;
private static final Method m30;
private static final Method m31;
private static final Method m32;
private static final Method m33;
private static final Method m34;
private static final Method m35;
private static final Method m36;
private static final Method m37;
private static final Method m38;
private static final Method m39;
private static final Method m40;
...
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("io.quarkiverse.cxf.it.ws.mtom.awt.server.ImageService").getMethod("downloadImage", Class.forName("java.lang.String"));
m4 = Class.forName("io.quarkiverse.cxf.it.ws.mtom.awt.server.ImageService").getMethod("uploadImage", Class.forName("java.awt.Image"), Class.forName("java.lang.String"));
m5 = Class.forName("javax.xml.ws.BindingProvider").getMethod("getEndpointReference", Class.forName("java.lang.Class"));
m6 = Class.forName("javax.xml.ws.BindingProvider").getMethod("getEndpointReference");
m7 = Class.forName("javax.xml.ws.BindingProvider").getMethod("getRequestContext");
m8 = Class.forName("javax.xml.ws.BindingProvider").getMethod("getResponseContext");
m9 = Class.forName("javax.xml.ws.BindingProvider").getMethod("getBinding");
m10 = Class.forName("java.io.Closeable").getMethod("close");
m11 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getConduit");
m12 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("setExecutor", Class.forName("java.util.concurrent.Executor"));
m13 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getContexts");
m14 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invokeWrapped", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("java.lang.String"), Class.forName("[Ljava.lang.Object;"));
m15 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invokeWrapped", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("javax.xml.namespace.QName"), Class.forName("[Ljava.lang.Object;"));
m16 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invokeWrapped", Class.forName("javax.xml.namespace.QName"), Class.forName("[Ljava.lang.Object;"));
m17 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invokeWrapped", Class.forName("java.lang.String"), Class.forName("[Ljava.lang.Object;"));
m18 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("setThreadLocalRequestContext", Boolean.TYPE);
m19 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("isThreadLocalRequestContext");
m20 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getConduitSelector");
m21 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("setConduitSelector", Class.forName("org.apache.cxf.endpoint.ConduitSelector"));
m22 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getBus");
m23 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getEndpoint");
m24 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("java.lang.String"), Class.forName("[Ljava.lang.Object;"));
m25 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("java.lang.String"), Class.forName("[Ljava.lang.Object;"));
m26 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"), Class.forName("java.util.Map"), Class.forName("org.apache.cxf.message.Exchange"));
m27 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"), Class.forName("java.util.Map"));
m28 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"));
m29 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("javax.xml.namespace.QName"), Class.forName("[Ljava.lang.Object;"));
m30 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"), Class.forName("org.apache.cxf.message.Exchange"));
m31 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"), Class.forName("java.util.Map"), Class.forName("org.apache.cxf.message.Exchange"));
m32 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"), Class.forName("java.util.Map"));
m33 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("org.apache.cxf.service.model.BindingOperationInfo"), Class.forName("[Ljava.lang.Object;"));
m34 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("invoke", Class.forName("org.apache.cxf.endpoint.ClientCallback"), Class.forName("javax.xml.namespace.QName"), Class.forName("[Ljava.lang.Object;"));
m35 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("destroy");
m36 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getOutFaultInterceptors");
m37 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getInInterceptors");
m38 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getInFaultInterceptors");
m39 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("getOutInterceptors");
m40 = Class.forName("org.apache.cxf.endpoint.Client").getMethod("onMessage", Class.forName("org.apache.cxf.message.Message"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
...
}
The static initializer triggers the initialization of java.awt.Image by calling Class.forName("java.awt.Image").
Let me ponder how can we fix this.
Hm... To be able to register a given proxy class for runtime initialization, I'd first need to know its name. But I am not finding any way how to get the proxy class name of the given set of interfaces.
@zakkak @galderz do you happen to have any idea whether it is possible to instruct GraalVM native-image to postpone the initialization of a generated proxy class for some specific set of interfaces?
I have updated the title and description with the information we now have about the root cause.
Hi @ppalaga, not sure how to tackle this. Just wondering if https://www.graalvm.org/22.3/reference-manual/native-image/guides/configure-dynamic-proxies/ (i.e. defining a dynamic proxy manually) could be of any help.
Are you defining any io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem for the said proxy?
Hi @ppalaga, not sure how to tackle this. Just wondering if https://www.graalvm.org/22.3/reference-manual/native-image/guides/configure-dynamic-proxies/ (i.e. defining a dynamic proxy manually) could be of any help.
Could you please explain, how it could help? My understanding is that what Quarkus does via NativeImageProxyDefinitionBuildItem has the very same effect as the JSON file mentioned in https://www.graalvm.org/22.3/reference-manual/native-image/guides/configure-dynamic-proxies/
We let GraalVM generate the proxy classes - that works well. We'd just need to be able to postpone the initialization for some of those (such as the ones referencing java.awt.Image).
Are you defining any
io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItemfor the said proxy?
Yes, we do produce a NativeImageProxyDefinitionBuildItem for every client SEI around here: https://github.com/quarkiverse/quarkus-cxf/blob/main/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java#L101-L102
Could you please explain, how it could help? My understanding is that what Quarkus does via NativeImageProxyDefinitionBuildItem has the very same effect as the JSON file mentioned in https://www.graalvm.org/22.3/reference-manual/native-image/guides/configure-dynamic-proxies/
Correct, I just was not sure whether you were using NativeImageProxyDefinitionBuildItem or not.
We'd just need to be able to postpone the initialization for some of those (such as the ones referencing java.awt.Image).
+1, unfortunately I am not sure how to achieve this. As you said, we would need to know the generated class name apriori._
@ppalaga IIRC it should also be possible to define a package that should be runtime initialized. Would that work in your case? As long as the package only has the generated proxies, you wouldn't (in theory) need to worry about the generated class names.
Interesting idea. From what I see in the output of -J-Djdk.proxy.ProxyGenerator.saveGeneratedFiles, it looks like also the package names are not 100% deterministic. Ours is jdk.proxy4 but I guess it could be some other number. Postponing the initialization of all generared proxy classes could cause some new conflicts.
Looking here (for Java 8, but newer also?), it says:
If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined in the same class loader and the same package with particular signers.
No idea if this will work, but if you define an empty interface in some package and you make the proxy implement that (even if it's empty), maybe you can end up controlling the package?
Looking here (for Java 8, but newer also?), it says:
If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined in the same class loader and the same package with particular signers.
No idea if this will work, but if you define an empty interface in some package and you make the proxy implement that (even if it's empty), maybe you can end up controlling the package?
That's a great hint! Thanks a lot @galderz!
Had a quick go and I think it might do the trick. Say we define a proxy handler like this:
static class DynamicInvocationHandler implements InvocationHandler
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
{
System.out.printf("Invoked method: %s%n", method.getName());
return 42;
}
}
And then you invoke:
Map proxyInstance = (Map) Proxy.newProxyInstance(
Proxies.class.getClassLoader()
, new Class[] { Map.class }
, new DynamicInvocationHandler()
);
proxyInstance.put("hello", "world");
System.out.println(proxyInstance.getClass());
It'll print:
Invoked method: put
class jdk.proxy1.$Proxy0
I defined an empty non-public interface in the package where the proxy handling code lives:
package lang.reflect;
interface Marker
{
}
I adjusted the proxy code to simply add the marker interface as one of the proxy interfaces:
Map proxyInstance = (Map) Proxy.newProxyInstance(
Proxies.class.getClassLoader()
, new Class[] { Map.class, Marker.class }
, new DynamicInvocationHandler()
);
proxyInstance.put("hello", "world");
System.out.println(proxyInstance.getClass());
When I execute that, the proxy package name is adjusted to the package where the non-public interface lives:
Invoked method: put
class lang.reflect.$Proxy1
I ran this with Java 17.
Thanks for verifying that, @galderz! When implementing it, it will be worth looking whether some of those user-provided interfaces happens to be non-public already. I think it is rather unlikely with web services, but we should check anyway. If so, then we cannot add our own marker interface, but we can try to use the user's one (outputting a warning about what we do).
Regardless of the above, it would still be handy to have some explicit controls about the init time of the generated proxy classes. Do you think it would be feasible to extend the GraalVM API to pass not only a list of interfaces but also a flag controlling the init time?
I mean whether this org.graalvm.nativeimage.hosted.RuntimeProxyCreation.register(Class... interfaces) method could get an overload like register(InitTime initTime, Class<?>... interfaces)
it would still be handy to have some explicit controls about the init time of the generated proxy classes. Do you think it would be feasible to extend the GraalVM API to pass not only a list of interfaces but also a flag controlling the init time?
I mean whether this org.graalvm.nativeimage.hosted.RuntimeProxyCreation.register(Class... interfaces) method could get an overload like
register(InitTime initTime, Class<?>... interfaces)
Any opinion on that @galderz @zakkak ?
I have a PoC proving that the idea of @galderz with the package private Marker interface is working in CXF. The only gotcha is that the Marker interface needs to be placed in the application code. Otherwise the JVM complains about mismatching class loaders.
Any opinion on that @galderz @zakkak ?
To reiterate my reaction to the first time you wrote it 👉 👀
Any opinion on that @galderz @zakkak ?
To reiterate my reaction to the first time you wrote it point_right eyes
I saw it, but I still wonder what it means? You are looking (a) whether the proposal makes sense or maybe (b) you are looking at others what they come up with or maybe you are looking at something else?
It just means I'm looking into what you proposed. IOW, need to see if it's feasible, if it makes sense, if there's some other way to achieve the same thing...etc.
Regardless of the above, it would still be handy to have some explicit controls about the init time of the generated proxy classes. Do you think it would be feasible to extend the GraalVM API to pass not only a list of interfaces but also a flag controlling the init time?
Did you try making ImageService, ImageData and/or any other classes that add the java.awt dependency to be runtime initialized? Unless I'm mistaken, a proxy's input is really the interface(s) that it implements, so if you have an issue the cause is likely in one of the interfaces, or classes they depend on.
Did you try making
ImageService,ImageDataand/or any other classes that add thejava.awtdependency to be runtime initialized? Unless I'm mistaken, a proxy's input is really the interface(s) that it implements, so if you have an issue the cause is likely in one of the interfaces, or classes they depend on.
I finally found some time to try this idea. I first created a simpler test in https://github.com/ppalaga/quarkus-cxf/commit/69ba2bdb6591eb10047cb383f6413ee0ea13b4df :
- The client interface has a single method
Result addOperands(Operands arg0); - Postponed init is forced for
Operandsin application.properties: https://github.com/ppalaga/quarkus-cxf/commit/69ba2bdb6591eb10047cb383f6413ee0ea13b4df#diff-f3072c584ba042d6e5ce5c2eaf1e64235ed539ebed2aff95dc4e4ac78c5cff8aR28
That's enough to reproduce the issue.
Then in the subsequent commit I tried to add all/some of the interfaces constituting the dynamic proxy to the postponed init. Those are defined around here, namely:
- The end user's service endpoint interface, here
ClientWithRuntimeInitializedPayload - jakarta.xml.ws.BindingProvider
- java.io.Closeable
- org.apache.cxf.endpoint.Client
Requesting runtime init for all of them solved the issue. Which I think would be kind of expected.
What I find quite strange is, that requesting runtime init for org.apache.cxf.endpoint.Client only helps too.
I have not found any combination without org.apache.cxf.endpoint.Client that would solve it.
Any idea @galderz what is so special about org.apache.cxf.endpoint.Client?
I even tried to change the ordering in the Proxy def, so that org.apache.cxf.endpoint.Client is not the last one, but it does not make any difference. org.apache.cxf.endpoint.Client is still the only that helps to solve it.
org.apache.cxf.endpoint.Client fixes also the original test with AWT image: https://github.com/ppalaga/quarkus-cxf/commit/i580-manual-fix-mtom-awt
Thinking loud: through org.apache.cxf.endpoint.Client, we can make all the client proxies available in the user app become runtime intialized. We cannot select only those which are known to need it. I wonder whether that can cause other issues. Init time conflicts could happen if some build time initialized class refers to either org.apache.cxf.endpoint.Client or the generated proxy class. The latter would be hard because the name is random and known only to GraalVM.
Ups, #769 does not fix this. Sorry for the noise
Thinking loud: through org.apache.cxf.endpoint.Client, we can make all the client proxies available in the user app become runtime intialized. We cannot select only those which are known to need it. I wonder whether that can cause other issues.
The only issue I can think of here is the potential drop of performance (both peak performance and startup time). You are essentially reducing the number of classes that are being build-time initialized, which can impact the number of optimizations applied and also adds the initialization of those classes to the run-time as well. Are these client proxies on the critical path?
Init time conflicts could happen if some build time initialized class refers to either org.apache.cxf.endpoint.Client or the generated proxy class. The latter would be hard because the name is random and known only to GraalVM.
Correct, I don't think this is a big issue and I think it would be easy to fix even if it happened.
Are these client proxies on the critical path?
Yes, in SOAP client apps, every request goes through them.
Yes, in SOAP client apps, every request goes through them.
In this case it might be worth investigating more.
@ppalaga I was checking the details of this issue again and I noticed that the error states that:
java.awt.Image the class was requested to be initialized at run time (Required for sun.text.bidi.BidiBase.NumericShapings). jdk.proxy4.$Proxy131 caused initialization of this class with the following trace
Checking sun.text.bidi.BidiBase.NumericShapings I see that it tries to load java.awt so I would start by requesting sun.text.bidi.BidiBase.NumericShapings instead of org.apache.cxf.endpoint.Client to be initialized at runtime.
Another thing I was thinking about was whether we could get away with a lot of issues if instead of requesting awt to be runtime initialized we requested for it to be runtime **re-**initialized (see https://github.com/oracle/graal/pull/4684 for some more info).
@zakkak thanks for looking into this again!
Checking
sun.text.bidi.BidiBase.NumericShapingsI see that it tries to loadjava.awtso I would start by requestingsun.text.bidi.BidiBase.NumericShapingsinstead oforg.apache.cxf.endpoint.Clientto be initialized at runtime.
But that would only help with java.awt.Image, woudn't it? But this issue can occur for any runtime initialized class, not only Image and so I'd prefer some general solution. In between I chose the strategy originally proposed by @galderz - i.e. to add a non-public interface to the Proxy definition so that the package of the generated proxy class gets predictable.
But this issue can occur for any runtime initialized class, not only Image and so I'd prefer some general solution.
True, it's another tradeoff between fine control of what gets runtime initialized and what not I guess.