Repository navigation
[Java] support copy non-serializable objects such as java.lang.Package #2941
Description
Search before asking
- I had searched in the issues and found no similar issues.
Version
I'm facing an issue copying Hibernate objects using fory.copy(object) whereas fory.deserialize(fory.serialize(object)) works fine for the same object.
java.lang.RuntimeException: Create sequential serializer failed, class: class org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader java.lang.UnsupportedOperationException: Class class java.lang.Package doesn't support serialization. at org.apache.fory.resolver.ClassResolver.getSerializerClass(ClassResolver.java:920) at org.apache.fory.resolver.ClassResolver.getSerializerClass(ClassResolver.java:854) at org.apache.fory.builder.BaseObjectCodecBuilder.lambda$getOrCreateSerializer$8(BaseObjectCodecBuilder.java:607) at org.apache.fory.builder.BaseObjectCodecBuilder.lambda$typeResolver$2(BaseObjectCodecBuilder.java:247) at org.apache.fory.builder.JITContext.asyncVisitFory(JITContext.java:159) at org.apache.fory.builder.BaseObjectCodecBuilder.typeResolver(BaseObjectCodecBuilder.java:247) at org.apache.fory.builder.BaseObjectCodecBuilder.typeResolver(BaseObjectCodecBuilder.java:243) at org.apache.fory.builder.BaseObjectCodecBuilder.getOrCreateSerializer(BaseObjectCodecBuilder.java:607) at org.apache.fory.builder.BaseObjectCodecBuilder.readOrGetSerializerForDeclaredType(BaseObjectCodecBuilder.java:2164) at org.apache.fory.builder.BaseObjectCodecBuilder.readChunk(BaseObjectCodecBuilder.java:2056) at org.apache.fory.builder.BaseObjectCodecBuilder.lambda$deserializeForMap$47e820f7$1(BaseObjectCodecBuilder.java:1995) at org.apache.fory.codegen.Expression$While.<init>(Expression.java:2209) at org.apache.fory.builder.BaseObjectCodecBuilder.deserializeForMap(BaseObjectCodecBuilder.java:1967) at org.apache.fory.builder.BaseObjectCodecBuilder.deserializeForNotNull(BaseObjectCodecBuilder.java:1662) at org.apache.fory.builder.BaseObjectCodecBuilder.deserializeForNotNull(BaseObjectCodecBuilder.java:1621) at org.apache.fory.builder.BaseObjectCodecBuilder.lambda$deserializeForNullable$26(BaseObjectCodecBuilder.java:1552) at org.apache.fory.builder.BaseObjectCodecBuilder.readRef(BaseObjectCodecBuilder.java:1572) at org.apache.fory.builder.BaseObjectCodecBuilder.deserializeForNullable(BaseObjectCodecBuilder.java:1552) at org.apache.fory.builder.ObjectCodecBuilder.lambda$deserializeGroup$135afeca$1(ObjectCodecBuilder.java:560) at org.apache.fory.codegen.ExpressionOptimizer.invokeGenerated(ExpressionOptimizer.java:49) at org.apache.fory.builder.ObjectCodecOptimizer.invokeGenerated(ObjectCodecOptimizer.java:138) at org.apache.fory.builder.ObjectCodecBuilder.deserializeGroup(ObjectCodecBuilder.java:577) at org.apache.fory.builder.ObjectCodecBuilder.buildDecodeExpression(ObjectCodecBuilder.java:465) at org.apache.fory.builder.BaseObjectCodecBuilder.genCode(BaseObjectCodecBuilder.java:264) at org.apache.fory.codegen.CompileUnit.getCode(CompileUnit.java:53) at org.apache.fory.codegen.JaninoUtils.toBytecode(JaninoUtils.java:81) at org.apache.fory.codegen.JaninoUtils.toBytecode(JaninoUtils.java:73) at org.apache.fory.codegen.CodeGenerator.compile(CodeGenerator.java:145) at org.apache.fory.builder.CodecUtils.loadOrGenCodecClass(CodecUtils.java:113) at org.apache.fory.builder.CodecUtils.lambda$loadOrGenObjectCodecClass$0(CodecUtils.java:53) at org.apache.fory.builder.CodecUtils.loadSerializer(CodecUtils.java:164) ... 172 common frames omitted Wrapped by: java.lang.RuntimeException: java.lang.UnsupportedOperationException: Class class java.lang.Package doesn't support serialization. at org.apache.fory.builder.CodecUtils.loadSerializer(CodecUtils.java:170) at org.apache.fory.builder.CodecUtils.loadOrGenObjectCodecClass(CodecUtils.java:50) at org.apache.fory.serializer.CodegenSerializer.loadCodegenSerializer(CodegenSerializer.java:49) ... 170 common frames omitted Wrapped by: java.lang.RuntimeException: Create sequential serializer failed, class: class org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader at org.apache.fory.serializer.CodegenSerializer.loadCodegenSerializer(CodegenSerializer.java:52) at org.apache.fory.resolver.ClassResolver.lambda$getObjectSerializerClass$3(ClassResolver.java:1024) at org.apache.fory.builder.JITContext.registerSerializerJITCallback(JITContext.java:121) at org.apache.fory.resolver.ClassResolver.getObjectSerializerClass(ClassResolver.java:1022) at org.apache.fory.resolver.ClassResolver.getSerializerClass(ClassResolver.java:976) at org.apache.fory.resolver.ClassResolver.getSerializerClass(ClassResolver.java:854) at org.apache.fory.resolver.ClassResolver.createSerializer(ClassResolver.java:1260) at org.apache.fory.resolver.ClassResolver.getOrUpdateClassInfo(ClassResolver.java:1182) at org.apache.fory.Fory.copyObject(Fory.java:1393) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.serializer.collection.CollectionSerializers$DefaultJavaCollectionSerializer.copy(CollectionSerializers.java:734) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.serializer.AbstractObjectSerializer.copyFields(AbstractObjectSerializer.java:788) at org.apache.fory.serializer.AbstractObjectSerializer.copy(AbstractObjectSerializer.java:702) at org.apache.fory.Fory.copyObject(Fory.java:1488) at org.apache.fory.Fory.copyObject(Fory.java:1448) at org.apache.fory.Fory.copy(Fory.java:1372) ... 135 common frames omitted Wrapped by: org.apache.fory.exception.CopyException: java.lang.RuntimeException: Create sequential serializer failed, class: class org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader at org.apache.fory.Fory.processCopyError(Fory.java:373)
Component(s)
Java
Minimal reproduce step
Trying to just copy the object fails.
What did you expect to see?
A successful deepCopy of the object.
What did you see instead?
The given exception.
Anything Else?
No response
Are you willing to submit a PR?
- I'm willing to submit a PR!
Activity
chaokunyang commentedon Nov 27, 2025
@thmd could you provide a reproduction example?
chaokunyang commentedon Nov 27, 2025
It seems you are copy a
java.lang.Packageobject, which is not serializable. I think we can just skip copy for such objects instead of raise errors[-]Fory copy fails while deepCopy works[/-][+]Fory copy fails for non-serializable java.lang.Package object[/+]thmd commentedon Nov 27, 2025
I think this reproduces the issue.
fory-hibernate-test.zip
thmd commentedon Nov 27, 2025
chaokunyang commentedon Dec 8, 2025
I have no plans to introduce another type dispatch mechnisim for copy, it will use same Serializer dispatch system
We could defer the exception to runtime. Instead of raise exception when get serializer, we could return a Serializer for any types. But for types which don't support serialization, we throw exception real serialization happens.
chaokunyang commentedon Dec 8, 2025
For any unserializable objects, the copy should just return original obejct
[-]Fory copy fails for non-serializable java.lang.Package object[/-][+][Java] support copy non-serializable objects such as java.lang.Package[/+]thmd commentedon Dec 14, 2025
I don't understand about Fory enough to tell what is meant by dispatch here. My big question if they are using similar/same code why is serialize succeed when copy fails? Does that have anything to do with transient fields? Does copy copies transient fields?
I think I can more meaningfully comment based on my use cases if I understand the difference between copy and serializae and why one succeed and one don't.
jim-parsons commentedon Dec 14, 2025
After some debug work, here is what i've found;
The
serialize/deserializefunction will ignore thetransientfield, butcopywon't , because thecopyfunction need to calculate all the field value , which currently depended onfory#serializefunction.So in this case, when copy filed
protected transient FastSessionServices fastSessionServices;it failed, because it has unsupport serialize filedClassLoaderThe serialize class wont handle
transientfieldchaokunyang commentedon Dec 15, 2025
@jim-parsons @thmd How about checking whether transient fields are serializable in constructor of AbstractObjectSerializer, if not, we just copy transient fields by ref directly
thmd commentedon Dec 15, 2025
I feel that copy original ref when it's not mutable breaks the contract of DeepCopy which is what this is useful for. I almost want two flags for copy if possible.
chaokunyang commentedon Dec 15, 2025
Transient fields always needs copy, some class use jdk writeObject/readObject to setup object invariant, Then set transient fields to correct values in those methods. If we don't copy transient fields, the copies object will be in wrong state.
chaokunyang commentedon Dec 15, 2025
@thmd Or maybe we can just refine the error message, and let users to register a CopySerializer for unsupported classes directly. This is much easier. By default, CopySerializer just return original ref. And user can subclass it to customize the copy logic
thmd commentedon Dec 15, 2025
There are valid cases for both wanting to DeepCopy an Object with transient fields and without. A DeepCopy with transient field is obviously most complete state copy. But the truth is often (to my knowledge) transient is used to mark we don't care about like here Hibernate framework harness which is often not useful in a DeepCopy use case.
DeepCopy objets are often used longterm e.g. in a cache which often makes transient references to ORM, Thread etc. invalid. Also most often DeepCopy is basically done using a serialize+deserialize pair which is what I was using before finding Fory.copy and such DeepCopy doesn't copy transient.
Now there are other cases people do want transient fields where they need a library with Copy (which is rare) or do something manually. So I think it's worth having a Copy without transient option as it allow someone to safely migrate from a serialize+deserialize to Fory.copy for the added performance (both cpu + memory overhead) gains.
I actually find returning original ref much more risky than skipping transient as DeepCopy is meant to give you an independent copy which original ref copy breaks and does so very silently.
chaokunyang commentedon Dec 15, 2025
If we enforce users to register a Serializer for unsupported classes directly, users will be aware of t. So there is no risk.
You can take HashMap as an example, it has size, modCount, table fields. They all are transient.
If we skip transient fields, we are copying nothing and will get NPE instead.
thmd commentedon Dec 15, 2025
I actually wasn't aware that HashMap has such fields as transient. It seems like I can't use Fory.copy as a drop-in replacement for a DeepCopy need currently served by a serialize+deserialize and my use cases is better served by that. It would be less efficient than Fory.copy but still better than JDK serialization.
HashMap could have made them regular (non-transient) field but still skip them in it's custom writeObject, which seems like a better design to me. Anyway it seems I know even lesser than what I thought about java serialization.
sjahedhussini commentedon Jun 5, 2026
Hi, I'm looking to make my first open source contribution, and this issue caught my attention. I'd like to take a stab at fixing it. From reading the stack trace and the discussion, it looks like the copy path is routing through the same serializer-lookup logic as serialize/deserialize, which hard-fails on non-serializable types like
java.lang.Package. I'm thinking the fix lives somewhere inClassResolverorAbstractObjectSerializer— would love any pointers from the maintainers on the preferred approach before I start. Happy to submit a PR.sjahedhussini commentedon Jun 20, 2026
Hello @chaokunyang! After reproducing the error, I traced the error to the method
ClassResolver.getSerializerClass(Class<?>, boolean)andThe following code/test produces it:
In order to solve this, my thinking is to let copy bypass this guard without weakening serialize. As in, route these classes to a serializer whose write/read still throw but whose copy works, similar to how
ForyCopyableSerializeralready gives copy distinct behavior. Before I implement, I wanted to check the design direction, since I saw the earlier discussion about transient fields vs. returning the original reference:For opaque JVM-managed classes like
java.lang.Package, would you prefercopy()attempt a field-based copy, or return the original reference? And does this bypass guard for copy approach sound right to you, or would you rather the fix be implemented somewhere else? Thank you, I just want to head in the right direction before opening a PR.itsCodeTide commentedon Oct 14, 2026
I’ve prepared an alternative fix in #4179 . It makes copy() return non-serializable JDK objects such as java.lang.Package by reference, while keeping serialization of those objects unsupported. This avoids trying to copy JVM-managed internals.
The change adds direct and nested copy coverage, plus a check that serialization still fails. I also saw that PR #3797 is open for this issue; this PR takes a different approach.