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

Native mode tests for serialization are failing #4148

Closed
jamesnetherton opened this issue Sep 29, 2022 · 13 comments
Closed

Native mode tests for serialization are failing #4148

jamesnetherton opened this issue Sep 29, 2022 · 13 comments
Labels
bug Something isn't working
Milestone

Comments

@jamesnetherton
Copy link
Contributor

Seems any tests for serialization in native mode are failing with an exception being thrown similar to this one:

SerializationConstructorAccessor class not found for declaringClass: java.util.Collections$EmptyList (targetConstructorClass: 
java.util.AbstractList). Usually adding java.util.Collections$EmptyList to serialization-config.json fixes the problem.]: ```

Collections.EMPTY_LIST is listed in the core set of classes bound for native serialization support.

https://github.com/apache/camel-quarkus/blob/main/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelSerializationProcessor.java#L47

2022-09-29 13:27:27,535 WARN  [org.apa.cam.com.jms.EndpointMessageListener] (Camel (camel-1) thread #3 - JmsConsumer[transferException]) Execution of JMS message listener failed. Caused by: [com.oracle.svm.core.jdk.UnsupportedFeatureError - SerializationConstructorAccessor class not found for declaringClass: java.util.Collections$EmptyList (targetConstructorClass: java.util.AbstractList). Usually adding java.util.Collections$EmptyList to serialization-config.json fixes the problem.]: com.oracle.svm.core.jdk.UnsupportedFeatureError: SerializationConstructorAccessor class not found for declaringClass: java.util.Collections$EmptyList (targetConstructorClass: java.util.AbstractList). Usually adding java.util.Collections$EmptyList to serialization-config.json fixes the problem.
        at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89)
        at com.oracle.svm.reflect.serialize.SerializationSupport.getSerializationConstructorAccessor(SerializationSupport.java:143)
        at jdk.internal.reflect.MethodAccessorGenerator.generateSerializationConstructor(MethodAccessorGenerator.java:48)
        at jdk.internal.reflect.ReflectionFactory.generateConstructor(ReflectionFactory.java:463)
        at jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:455)
        at java.io.ObjectStreamClass.getSerializableConstructor(ObjectStreamClass.java:1443)
        at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:412)
        at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:384)
        at java.security.AccessController.executePrivileged(AccessController.java:169)
        at java.security.AccessController.doPrivileged(AccessController.java:318)
        at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:384)
        at java.io.ObjectStreamClass$Caches$1.computeValue(ObjectStreamClass.java:110)
        at java.io.ObjectStreamClass$Caches$1.computeValue(ObjectStreamClass.java:107)
        at java.io.ClassCache$1.computeValue(ClassCache.java:73)
        at java.io.ClassCache$1.computeValue(ClassCache.java:70)
        at java.lang.ClassValue.get(JavaLangSubstitutions.java:588)
        at java.io.ClassCache.get(ClassCache.java:84)
        at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:363)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1137)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
        at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:443)
        at java.lang.Throwable.writeObject(Throwable.java:1014)
        at java.lang.reflect.Method.invoke(Method.java:568)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
        at org.apache.activemq.artemis.jms.client.ActiveMQObjectMessage.setObject(ActiveMQObjectMessage.java:118)
        at org.apache.activemq.artemis.jms.client.ActiveMQSession.createObjectMessage(ActiveMQSession.java:216)
        at org.apache.camel.component.jms.JmsBinding.createJmsMessage(JmsBinding.java:516)
        at org.apache.camel.component.jms.JmsBinding.makeJmsMessage(JmsBinding.java:344)
        at org.apache.camel.component.jms.EndpointMessageListener.lambda$sendReply$0(EndpointMessageListener.java:401)
        at org.apache.camel.component.jms.JmsConfiguration$CamelJmsTemplate.doSendToDestination(JmsConfiguration.java:616)
        at org.apache.camel.component.jms.JmsConfiguration$CamelJmsTemplate.lambda$send$3(JmsConfiguration.java:602)
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:504)
        at org.apache.camel.component.jms.JmsConfiguration$CamelJmsTemplate.send(JmsConfiguration.java:602)
        at org.apache.camel.component.jms.EndpointMessageListener.sendReply(EndpointMessageListener.java:400)
        at org.apache.camel.component.jms.EndpointMessageListener$EndpointMessageListenerAsyncCallback.done(EndpointMessageListener.java:233)
        at org.apache.camel.component.jms.EndpointMessageListener.onMessage(EndpointMessageListener.java:136)
        at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:736)
        at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:696)
        at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:674)
        at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:331)
        at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270)
        at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1237)
        at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1227)
        at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1120)
@jamesnetherton jamesnetherton added the bug Something isn't working label Sep 29, 2022
@jamesnetherton jamesnetherton added this to the 2.14 milestone Sep 29, 2022
@jamesnetherton jamesnetherton changed the title [Quarkus 2.14.0] Native mode tests for serialization are failing Native mode tests for serialization are failing Oct 5, 2022
@jamesnetherton
Copy link
Contributor Author

@zakkak any ideas on recent changes that may have had an impact on Serialization in native mode? I see some commits related to native that got backported to 2.13.1.

@zakkak
Copy link

zakkak commented Oct 5, 2022

@zakkak any ideas on recent changes that may have had an impact on Serialization in native mode? I see some commits related to native that got backported to 2.13.1.

Most of these commits change behavior only when using GraalVM / Mandrel 22.3 which is not yet released. So they should not affect your tests at this point.

The only possibly related change could be quarkusio/quarkus@e363914#diff-7d1388bbdd07a9362ef261153d5cc4cbccd0f5f5f95446ad3f647f9cd38768bcL471-R469

Do I understand correctly that things are working with 2.13.0 but not with 2.13.1 using the same GraalVM / Mandrel version?

@jamesnetherton
Copy link
Contributor Author

Do I understand correctly that things are working with 2.13.0 but not with 2.13.1 using the same GraalVM / Mandrel version?

Yes, exactly

@zakkak
Copy link

zakkak commented Oct 6, 2022

OK, could you please provide the steps to reproduce this issue (ideally with the option to use local quarkus builds, as well i.e. 999-SNAPSHOT) so that I can have a closer look?

@jamesnetherton
Copy link
Contributor Author

@zakkak try this branch on my fork. It's set up to point at Quarkus 999-SNAPSHOT:

git clone git@github.com:jamesnetherton/camel-quarkus.git -b quarkus-main
./mvnw clean install -Dquickly -T1C
cd integration-tests/http
../../mvnw clean verify -Dnative -Ddocker

You can omit the -Ddocker part if you have GraalVM 22.2.0 installed locally.

@zakkak
Copy link

zakkak commented Oct 6, 2022

I confirm that quarkusio/quarkus@e363914#diff-7d1388bbdd07a9362ef261153d5cc4cbccd0f5f5f95446ad3f647f9cd38768bcL471-R469 is the one causing the regression here but the issue seems to originate from quarkusio/gizmo@091fe32.

quarkus-camel registers java.util.Collections$EmptyList for serialization, but because the class name starts with java. it's not getting registered properly (due to quarkusio/gizmo@091fe32)

@Sanne can you please have a look?

@Sanne
Copy link
Contributor

Sanne commented Oct 6, 2022

@zakkak coul you elaborate on "not getting registered properly" ? What's happening?

@zakkak
Copy link

zakkak commented Oct 6, 2022

@zakkak coul you elaborate on "not getting registered properly" ? What's happening?

After looking a bit more into it, the issue is the following.

When the class name starts with java. gizmo gets the class directly (not through reflection):

    private static void registerClass35(org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess var0) {
        try {
            Collections.EmptyList.class.getDeclaredConstructors();
            Collections.EmptyList.class.getDeclaredMethods();
            Collections.EmptyList.class.getDeclaredFields();
            Class[] var1 = new Class[]{Collections.EmptyList.class};
            RuntimeReflection.register(var1);
            registerSerializationForClass(Collections.EmptyList.class);
        } catch (Throwable var2) {
        }

    } 

However, Collections.EmptyList is private, as a result this is not going to work. Note here that we don't see any error due to quarkusio/quarkus#26162. The actual exception is:

java.lang.IllegalAccessError: failed to access class java.util.Collections$EmptyList from class io.quarkus.runner.Feature (java.util.Collections$EmptyList is in module java.base of loader 'bootstrap'; io.quarkus.runner.Feature is in unnamed module of loader com.oracle.svm.hosted.NativeImageClassLoaderSupport$ClassPathClassLoader @6d4b1c02)
	at io.quarkus.runner.Feature.registerClass35(Unknown Source)
	at io.quarkus.runner.Feature.beforeAnalysis(Unknown Source)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.lambda$runPointsToAnalysis$9(NativeImageGenerator.java:722)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.FeatureHandler.forEachFeature(FeatureHandler.java:78)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:722)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:564)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:521)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:407)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:585)
	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:128)

In contrast when registering a class that is not under java. the register method uses Class.forName which works for private methods as well and looks like this:

    private static void registerClass0(org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess var0) {
        try {
            ClassLoader var1 = Thread.currentThread().getContextClassLoader();
            Class var2 = Class.forName("org.apache.commons.pool2.impl.DefaultEvictionPolicy", (boolean)0, var1);
            Constructor[] var4 = var2.getDeclaredConstructors();
            var2.getDeclaredMethods();
            var2.getDeclaredFields();
            Class[] var3 = new Class[]{var2};
            RuntimeReflection.register(var3);
            RuntimeReflection.register((Executable[])var4);
        } catch (Throwable var5) {
        }

    }

So I think that we either need to drop the java. prefix optimization from gizmo or start checking if the class is private or not as well.

@Sanne
Copy link
Contributor

Sanne commented Oct 6, 2022

thanks for the explanation - tricky. Yes I can see how my previous optimisation would break this - but there's also the case for useTccl being set to false: what if rather than invoking io.quarkus.gizmo.BytecodeCreatorImpl#loadClassFromTCCL(String) one was using loadClass(String) ?
Especially when loading JDK types I would expect both invocations to be valid, and yet only loadClassFromTCCL would have used reflection. Plus I'd expect that when registering for Reflection/Serialization, it shouldn't matter which classloader is being used.

So rather than reverting I think gizmo should be fixed to handle private types correctly whatever the choice of classloader.

@zakkak
Copy link

zakkak commented Oct 6, 2022

So rather than reverting I think gizmo should be fixed to handle private types correctly whatever the choice of classloader.

+1

I created quarkusio/quarkus#28431 to track this in Quarkus as well, since it doesn't affect only camel-quarkus.

@geoand
Copy link

geoand commented Oct 7, 2022

Could quarkusio/quarkus#12486 (comment) also be related?

@Sanne
Copy link
Contributor

Sanne commented Oct 7, 2022

@geoand yes: it's a non-public class, and in package java. : same issue.

@geoand
Copy link

geoand commented Oct 7, 2022

Thanks for confirming @Sanne

jamesnetherton added a commit to jamesnetherton/camel-quarkus that referenced this issue Oct 7, 2022
jamesnetherton added a commit to jamesnetherton/camel-quarkus that referenced this issue Oct 10, 2022
jamesnetherton added a commit to jamesnetherton/camel-quarkus that referenced this issue Oct 12, 2022
jamesnetherton added a commit to jamesnetherton/camel-quarkus that referenced this issue Oct 12, 2022
jamesnetherton added a commit to jamesnetherton/camel-quarkus that referenced this issue Oct 13, 2022
jamesnetherton added a commit that referenced this issue Oct 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants