Description
The problem found during testing vertx-ignite project with new version of Vert.x framework.
Binary marshaller fails on deserialization of object with writeReplace() because deserialized object can't be assigned to field due to a type incompatibility.
During setting field value the following checking will be failed in UnsafeObjectFieldAccessorImpl.set(Object var1, Object var2) method (see comment):
public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException { this.ensureObj(var1); if (this.isFinal) { this.throwFinalFieldIllegalAccessException(var2); } // HERE: Field type isn't assignable from object type. if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) { this.throwSetIllegalArgumentException(var2); } unsafe.putObject(var1, this.fieldOffset, var2); }
The following error will be logged:
class org.apache.ignite.binary.BinaryObjectException: Failed to deserialize object [typeName=org.apache.ignite.internal.binary.BinaryMarshallerReplaceObjectTest$TestObject] at org.apache.ignite.internal.binary.BinaryClassDescriptor.read(BinaryClassDescriptor.java:874) at org.apache.ignite.internal.binary.BinaryReaderExImpl.deserialize0(BinaryReaderExImpl.java:1762) at org.apache.ignite.internal.binary.BinaryReaderExImpl.deserialize(BinaryReaderExImpl.java:1714) at org.apache.ignite.internal.binary.BinaryObjectImpl.deserializeValue(BinaryObjectImpl.java:797) at org.apache.ignite.internal.binary.BinaryObjectImpl.deserialize(BinaryObjectImpl.java:639) at org.apache.ignite.internal.binary.BinaryMarshallerReplaceObjectTest.testUnmarshal(BinaryMarshallerReplaceObjectTest.java:35) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at junit.framework.TestCase.runTest(TestCase.java:176) at org.apache.ignite.testframework.junits.GridAbstractTest.runTestInternal(GridAbstractTest.java:2000) at org.apache.ignite.testframework.junits.GridAbstractTest.access$000(GridAbstractTest.java:132) at org.apache.ignite.testframework.junits.GridAbstractTest$5.run(GridAbstractTest.java:1915) at java.lang.Thread.run(Thread.java:745) Caused by: class org.apache.ignite.binary.BinaryObjectException: Failed to read field [name=val] at org.apache.ignite.internal.binary.BinaryFieldAccessor.read(BinaryFieldAccessor.java:168) at org.apache.ignite.internal.binary.BinaryClassDescriptor.read(BinaryClassDescriptor.java:843) ... 14 more Caused by: java.lang.IllegalArgumentException: Can not set org.apache.ignite.internal.binary.BinaryMarshallerReplaceObjectTest$Intf field org.apache.ignite.internal.binary.BinaryMarshallerReplaceObjectTest$TestObject.val to org.apache.ignite.internal.binary.BinaryMarshallerReplaceObjectTest$Cls at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168) at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81) at java.lang.reflect.Field.set(Field.java:741) at org.apache.ignite.internal.binary.BinaryFieldAccessor$DefaultFinalClassAccessor.read0(BinaryFieldAccessor.java:683) at org.apache.ignite.internal.binary.BinaryFieldAccessor.read(BinaryFieldAccessor.java:164) ... 15 more
Minimal reproducer:
package org.apache.ignite.internal.binary; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.binary.BinaryIdMapper; import org.apache.ignite.binary.BinaryNameMapper; import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.binary.BinarySerializer; import org.apache.ignite.binary.BinaryTypeConfiguration; import org.apache.ignite.configuration.BinaryConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager; import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.logger.NullLogger; import org.apache.ignite.marshaller.MarshallerContextTestImpl; import org.apache.ignite.spi.discovery.DiscoverySpiCustomMessage; import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; import org.apache.ignite.testframework.junits.GridTestKernalContext; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; public class BinaryMarshallerReplaceObjectTest extends GridCommonAbstractTest { public void testUnmarshal() throws Exception { BinaryMarshaller marsh = binaryMarshaller(null, null, null, Arrays.asList( new BinaryTypeConfiguration(TestObject.class.getName())), null); TestObject obj = new TestObject(); BinaryObject po = marshal(obj, marsh); po.deserialize(); } interface Intf { long value(); } static class TestObject { Intf val = new IntfImpl(); } static class IntfImpl extends Cls implements Intf { @Override public long value() { return longValue(); } } static class Cls { long val; public long longValue() { return val; } private Object writeReplace() { return new SerializationProxy(this); } private static class SerializationProxy implements Serializable { private final long val; SerializationProxy(Cls a) { val = a.longValue(); } private Object readResolve() { Cls a = new Cls(); a.val = val; return a; } } } protected BinaryMarshaller binaryMarshaller( BinaryNameMapper nameMapper, BinaryIdMapper mapper, BinarySerializer serializer, Collection<BinaryTypeConfiguration> cfgs, Collection<String> excludedClasses ) throws IgniteCheckedException { IgniteConfiguration iCfg = new IgniteConfiguration(); BinaryConfiguration bCfg = new BinaryConfiguration(); bCfg.setNameMapper(nameMapper); bCfg.setIdMapper(mapper); bCfg.setSerializer(serializer); bCfg.setCompactFooter(true); bCfg.setTypeConfigurations(cfgs); iCfg.setBinaryConfiguration(bCfg); iCfg.setClientMode(false); iCfg.setDiscoverySpi(new TcpDiscoverySpi() { @Override public void sendCustomEvent(DiscoverySpiCustomMessage msg) throws IgniteException { //No-op. } }); BinaryContext ctx = new BinaryContext(BinaryCachingMetadataHandler.create(), iCfg, new NullLogger()); BinaryMarshaller marsh = new BinaryMarshaller(); MarshallerContextTestImpl marshCtx = new MarshallerContextTestImpl(null, excludedClasses); GridTestKernalContext kernCtx = new GridTestKernalContext(log, iCfg); kernCtx.add(new GridDiscoveryManager(kernCtx)); marshCtx.onMarshallerProcessorStarted(kernCtx, null); marsh.setContext(marshCtx); IgniteUtils.invoke(BinaryMarshaller.class, marsh, "setBinaryContext", ctx, iCfg); return marsh; } /** * @param obj Object. * @param marsh Marshaller. * @return Binary object. */ private <T> BinaryObjectImpl marshal(T obj, BinaryMarshaller marsh) throws IgniteCheckedException { byte[] bytes = marsh.marshal(obj); return new BinaryObjectImpl(U.<GridBinaryMarshaller>field(marsh, "impl").context(), bytes, 0); } }