Problem:
We are currently using Caucho Hessian 3.2.0 in combination with Java 8 and Java 11 applications. This works fine, but we want to migrate to Java 17 and we get following systen.out message:
java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String java.lang.StackTraceElement.classLoaderName accessible: module java.base does not "opens java.lang" to unnamed module @78e03bb5
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at com.caucho.hessian.io.JavaDeserializer.getFieldMap(JavaDeserializer.java:299)
at com.caucho.hessian.io.JavaDeserializer.<init>(JavaDeserializer.java:77)
at com.caucho.hessian.io.StackTraceElementDeserializer.<init>(StackTraceElementDeserializer.java:60)
at com.caucho.hessian.io.SerializerFactory.<clinit>(SerializerFactory.java:627)
at com.caucho.hessian.io.AbstractHessianOutput.findSerializerFactory(AbstractHessianOutput.java:95)
at com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:486)
at Main.serializeWithHessian(Main.java:23)
at Main.main(Main.java:13)
There is a work-around by adding the following JVM param:
--add-opens java.base/java.lang=ALL-UNNAMED
We dont want to use this flag in all our productive applications, so we tried to upgrade Hessian to its newest version 4.0.66. Basicly this works for the most common use cases but doesn't work when we serialize data having same object instances multiple times.
I created a small example application: https://github.com/MatWein/hessian-test
This application reads some java serialized test data from its classpath and tries to serialize and deserialize it with Hessian. If you run this application using Hessian 3.2.0 it works for all Java versions (8, 11 and 17 if you set the JVM flag above) in run&debug modes. But I cannot get it work with Hessian 4.0.66 because following error occures:
Exception in thread "main" com.caucho.hessian.io.HessianFieldException: de.test.TestData.field29: de.test.PaymentType cannot be assigned from null
at com.caucho.hessian.io.FieldDeserializer2FactoryUnsafe.logDeserializeError(FieldDeserializer2FactoryUnsafe.java:538)
at com.caucho.hessian.io.FieldDeserializer2FactoryUnsafe$ObjectFieldDeserializer.deserialize(FieldDeserializer2FactoryUnsafe.java:169)
at com.caucho.hessian.io.UnsafeDeserializer.readObject(UnsafeDeserializer.java:237)
at com.caucho.hessian.io.UnsafeDeserializer.readObject(UnsafeDeserializer.java:148)
at com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2202)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2123)
at com.caucho.hessian.io.CollectionDeserializer.readLengthList(CollectionDeserializer.java:93)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2050)
at Main.deserializeWithHessian(Main.java:32)
at Main.main(Main.java:15)
Caused by: java.lang.IndexOutOfBoundsException: Index 13 out of bounds for length 13
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:361)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1810)
at com.caucho.hessian.io.FieldDeserializer2FactoryUnsafe$ObjectFieldDeserializer.deserialize(FieldDeserializer2FactoryUnsafe.java:165)
... 8 more
ATTENTION: We have a different runtime behaviour between running and debugging the app (dont know why, but Hessian seems to use Unsafe class, Weak&Soft references and so on).
For me, it looks like an internal bug in Hessian. It is using the class com.caucho.hessian.util.IdentityIntMap on serialization site, but when deserializing the client cannot find the object reference index.
Question: Is there any way to get Hessian 4.0.66 working with Java 8, 11 and 17 (or at least with Java 11 and 17)?
Finally, found the issue after lot of debugging. The issue is with Enum fields having
nullvalues.In Hessian
3.x.x, it is allowing de-serialization ofnullvalue by default for Enum type as checked.But in Hessian
4.x.x, they have changed the approach of evaluating enum values and didn't allow the de-serialization ofnullvalue for Enum type as checked.Note: I have used 4.0.66 Hessian. It will work with all versions of (JDK 8, 11, 17) (already tested)
Fix:
transientkeyword forPaymentType,GenderType&ActivationStateas they are coming withnullvalues and should be avoided being part of de-serialization.Note: Before putting
transientfor Enum fields, please make sure to select fields that you think are not needed as part of serialization and you are sure that data will be coming as null for those Enum fields when de-serialization will happen.Output with JDK 11:
Output with JDK 8:
Output with JDK 17:
Second solution: Make sure to send the non-null values in those appropriate Enum fields to avoid facing the below error.
Update:
jackson-databinddependency in pom.xml of the app, Hessian surprisingly started to deserialize withnullvalues for Enum type.Just add this dependency in POM:
@HessianUnshared(that you suggested) annotation will fix the issue.