/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.nio.serialization; import com.hazelcast.config.SerializationConfig; import com.hazelcast.config.SerializerConfig; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.internal.serialization.PortableContext; import com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder; import com.hazelcast.spi.serialization.SerializationService; import com.hazelcast.test.HazelcastSerialClassRunner; import com.hazelcast.test.annotation.QuickTest; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.io.IOException; import java.nio.ByteOrder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(HazelcastSerialClassRunner.class) @Category(QuickTest.class) public class PortableTest { static final int PORTABLE_FACTORY_ID = TestSerializationConstants.PORTABLE_FACTORY_ID; static final int IDENTIFIED_FACTORY_ID = TestSerializationConstants.DATA_SERIALIZABLE_FACTORY_ID; @Test public void testBasics() { testBasics(ByteOrder.BIG_ENDIAN, false); } @Test public void testBasicsLittleEndian() { testBasics(ByteOrder.LITTLE_ENDIAN, false); } @Test public void testBasicsNativeOrder() { testBasics(ByteOrder.nativeOrder(), false); } @Test public void testBasicsNativeOrderUsingUnsafe() { testBasics(ByteOrder.nativeOrder(), true); } private void testBasics(ByteOrder order, boolean allowUnsafe) { final SerializationService serializationService = createSerializationService(1, order, allowUnsafe); final SerializationService serializationService2 = createSerializationService(2, order, allowUnsafe); Data data; NamedPortable[] nn = new NamedPortable[5]; for (int i = 0; i < nn.length; i++) { nn[i] = new NamedPortable("named-portable-" + i, i); } NamedPortable np = nn[0]; data = serializationService.toData(np); assertEquals(np, serializationService.toObject(data)); assertEquals(np, serializationService2.toObject(data)); InnerPortable inner = new InnerPortable(new byte[]{0, 1, 2}, new char[]{'c', 'h', 'a', 'r'}, new short[]{3, 4, 5}, new int[]{9, 8, 7, 6}, new long[]{0, 1, 5, 7, 9, 11}, new float[]{0.6543f, -3.56f, 45.67f}, new double[]{456.456, 789.789, 321.321}, nn); data = serializationService.toData(inner); assertEquals(inner, serializationService.toObject(data)); assertEquals(inner, serializationService2.toObject(data)); MainPortable main = new MainPortable((byte) 113, true, 'x', (short) -500, 56789, -50992225L, 900.5678f, -897543.3678909d, "this is main portable object created for testing!", inner); data = serializationService.toData(main); assertEquals(main, serializationService.toObject(data)); assertEquals(main, serializationService2.toObject(data)); } static InternalSerializationService createSerializationService(int version) { return createSerializationService(version, ByteOrder.BIG_ENDIAN, false); } static InternalSerializationService createSerializationService(int version, ByteOrder order, boolean allowUnsafe) { return new DefaultSerializationServiceBuilder() .setUseNativeByteOrder(false).setAllowUnsafe(allowUnsafe).setByteOrder(order).setPortableVersion(version) .addPortableFactory(PORTABLE_FACTORY_ID, new TestPortableFactory()) .addDataSerializableFactory(IDENTIFIED_FACTORY_ID, new TestDataSerializableFactory()).build(); } static ClassDefinition createNamedPortableClassDefinition(int portableVersion) { ClassDefinitionBuilder builder = new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, TestSerializationConstants.NAMED_PORTABLE, portableVersion); builder.addUTFField("name"); builder.addIntField("myint"); return builder.build(); } @Test public void testRawData() { int portableVersion = 1; final InternalSerializationService serializationService = createSerializationService(1); RawDataPortable p = new RawDataPortable(System.currentTimeMillis(), "test chars".toCharArray(), new NamedPortable("named portable", 34567), 9876, "Testing raw portable", new ByteArrayDataSerializable("test bytes".getBytes())); ClassDefinitionBuilder builder = new ClassDefinitionBuilder(p.getFactoryId(), p.getClassId(), portableVersion); builder.addLongField("l").addCharArrayField("c").addPortableField("p", createNamedPortableClassDefinition(portableVersion)); serializationService.getPortableContext().registerClassDefinition(builder.build()); final Data data = serializationService.toData(p); assertEquals(p, serializationService.toObject(data)); } @Test public void testRawDataWithoutRegistering() { final SerializationService serializationService = createSerializationService(1); RawDataPortable p = new RawDataPortable(System.currentTimeMillis(), "test chars".toCharArray(), new NamedPortable("named portable", 34567), 9876, "Testing raw portable", new ByteArrayDataSerializable("test bytes".getBytes())); final Data data = serializationService.toData(p); assertEquals(p, serializationService.toObject(data)); } @Test(expected = HazelcastSerializationException.class) public void testRawDataInvalidWrite() { int portableVersion = 1; final InternalSerializationService serializationService = createSerializationService(1); RawDataPortable p = new InvalidRawDataPortable(System.currentTimeMillis(), "test chars".toCharArray(), new NamedPortable("named portable", 34567), 9876, "Testing raw portable", new ByteArrayDataSerializable("test bytes".getBytes())); ClassDefinitionBuilder builder = new ClassDefinitionBuilder(p.getFactoryId(), p.getClassId(), portableVersion); builder.addLongField("l").addCharArrayField("c").addPortableField("p", createNamedPortableClassDefinition(portableVersion)); serializationService.getPortableContext().registerClassDefinition(builder.build()); final Data data = serializationService.toData(p); assertEquals(p, serializationService.toObject(data)); } @Test(expected = HazelcastSerializationException.class) public void testRawDataInvalidRead() { int portableVersion = 1; final InternalSerializationService serializationService = createSerializationService(1); RawDataPortable p = new InvalidRawDataPortable2(System.currentTimeMillis(), "test chars".toCharArray(), new NamedPortable("named portable", 34567), 9876, "Testing raw portable", new ByteArrayDataSerializable("test bytes".getBytes())); ClassDefinitionBuilder builder = new ClassDefinitionBuilder(p.getFactoryId(), p.getClassId(), portableVersion); builder.addLongField("l").addCharArrayField("c").addPortableField("p", createNamedPortableClassDefinition(portableVersion)); serializationService.getPortableContext().registerClassDefinition(builder.build()); final Data data = serializationService.toData(p); assertEquals(p, serializationService.toObject(data)); } @Test public void testClassDefinitionConfigWithErrors() throws Exception { SerializationConfig serializationConfig = new SerializationConfig(); serializationConfig.addPortableFactory(PORTABLE_FACTORY_ID, new TestPortableFactory()); serializationConfig.setPortableVersion(1); serializationConfig.addClassDefinition( new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, TestSerializationConstants.RAW_DATA_PORTABLE, 1) .addLongField("l").addCharArrayField("c").addPortableField("p", createNamedPortableClassDefinition(1)).build()); try { new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build(); fail("Should throw HazelcastSerializationException!"); } catch (HazelcastSerializationException ignored) { } new DefaultSerializationServiceBuilder().setConfig(serializationConfig).setCheckClassDefErrors(false).build(); // -- OR -- serializationConfig.setCheckClassDefErrors(false); new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build(); } @Test public void testClassDefinitionConfig() throws Exception { int portableVersion = 1; SerializationConfig serializationConfig = new SerializationConfig(); serializationConfig.addPortableFactory(PORTABLE_FACTORY_ID, new TestPortableFactory()); serializationConfig.setPortableVersion(portableVersion); serializationConfig .addClassDefinition( new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, TestSerializationConstants.RAW_DATA_PORTABLE, portableVersion) .addLongField("l").addCharArrayField("c").addPortableField("p", createNamedPortableClassDefinition(portableVersion)).build()) .addClassDefinition( new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, TestSerializationConstants.NAMED_PORTABLE, portableVersion) .addUTFField("name").addIntField("myint").build()); SerializationService serializationService = new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build(); RawDataPortable p = new RawDataPortable(System.currentTimeMillis(), "test chars".toCharArray(), new NamedPortable("named portable", 34567), 9876, "Testing raw portable", new ByteArrayDataSerializable("test bytes".getBytes())); final Data data = serializationService.toData(p); assertEquals(p, serializationService.toObject(data)); } @Test public void testPortableNestedInOthers() { SerializationService serializationService = createSerializationService(1); Object o1 = new ComplexDataSerializable(new NamedPortable("test-portable", 137), new ByteArrayDataSerializable("test-data-serializable".getBytes()), new ByteArrayDataSerializable("test-data-serializable-2".getBytes())); Data data = serializationService.toData(o1); SerializationService serializationService2 = createSerializationService(2); Object o2 = serializationService2.toObject(data); assertEquals(o1, o2); } //https://github.com/hazelcast/hazelcast/issues/1096 @Test public void test_1096_ByteArrayContentSame() { SerializationService ss = new DefaultSerializationServiceBuilder() .addPortableFactory(PORTABLE_FACTORY_ID, new TestPortableFactory()).build(); assertRepeatedSerialisationGivesSameByteArrays(ss, new NamedPortable("issue-1096", 1096)); assertRepeatedSerialisationGivesSameByteArrays(ss, new InnerPortable(new byte[3], new char[5], new short[2], new int[10], new long[7], new float[9], new double[1], new NamedPortable[]{new NamedPortable("issue-1096", 1096)})); assertRepeatedSerialisationGivesSameByteArrays(ss, new RawDataPortable(1096L, "issue-1096".toCharArray(), new NamedPortable("issue-1096", 1096), 1096, "issue-1096", new ByteArrayDataSerializable(new byte[1]))); } private static void assertRepeatedSerialisationGivesSameByteArrays(SerializationService ss, Portable p) { Data data1 = ss.toData(p); for (int k = 0; k < 100; k++) { Data data2 = ss.toData(p); assertEquals(data1, data2); } } //https://github.com/hazelcast/hazelcast/issues/2172 @Test public void test_issue2172_WritePortableArray() { final SerializationService ss = new DefaultSerializationServiceBuilder().setInitialOutputBufferSize(16).build(); final TestObject2[] testObject2s = new TestObject2[100]; for (int i = 0; i < testObject2s.length; i++) { testObject2s[i] = new TestObject2(); } final TestObject1 testObject1 = new TestObject1(testObject2s); ss.toData(testObject1); } @Test public void testClassDefinitionLookupBigEndianHeapData() throws IOException { InternalSerializationService ss = new DefaultSerializationServiceBuilder() .setByteOrder(ByteOrder.BIG_ENDIAN) .build(); testClassDefinitionLookup(ss); } @Test public void testClassDefinitionLookupLittleEndianHeapData() throws IOException { InternalSerializationService ss = new DefaultSerializationServiceBuilder() .setByteOrder(ByteOrder.LITTLE_ENDIAN) .build(); testClassDefinitionLookup(ss); } @Test public void testClassDefinitionLookupNativeOrderHeapData() throws IOException { InternalSerializationService ss = new DefaultSerializationServiceBuilder() .setUseNativeByteOrder(true) .build(); testClassDefinitionLookup(ss); } static void testClassDefinitionLookup(InternalSerializationService ss) throws IOException { NamedPortableV2 p = new NamedPortableV2("test-portable", 123456789, 500); Data data = ss.toData(p); PortableContext portableContext = ss.getPortableContext(); ClassDefinition cd = portableContext.lookupClassDefinition(data); assertNotNull(cd); assertEquals(p.getFactoryId(), cd.getFactoryId()); assertEquals(p.getClassId(), cd.getClassId()); assertEquals(p.getClassVersion(), cd.getVersion()); } @Test public void testSerializationService_createPortableReader() throws IOException { InternalSerializationService serializationService = new DefaultSerializationServiceBuilder().build(); long timestamp1 = System.nanoTime(); ChildPortableObject child = new ChildPortableObject(timestamp1); long timestamp2 = System.currentTimeMillis(); ParentPortableObject parent = new ParentPortableObject(timestamp2, child); long timestamp3 = timestamp1 + timestamp2; GrandParentPortableObject grandParent = new GrandParentPortableObject(timestamp3, parent); Data data = serializationService.toData(grandParent); PortableReader reader = serializationService.createPortableReader(data); assertEquals(grandParent.timestamp, reader.readLong("timestamp")); assertEquals(parent.timestamp, reader.readLong("child.timestamp")); assertEquals(child.timestamp, reader.readLong("child.child.timestamp")); } @Test public void testClassDefinition_getNestedField() throws IOException { InternalSerializationService serializationService = new DefaultSerializationServiceBuilder().build(); PortableContext portableContext = serializationService.getPortableContext(); ChildPortableObject child = new ChildPortableObject(System.nanoTime()); ParentPortableObject parent = new ParentPortableObject(System.currentTimeMillis(), child); GrandParentPortableObject grandParent = new GrandParentPortableObject(System.nanoTime(), parent); Data data = serializationService.toData(grandParent); ClassDefinition classDefinition = portableContext.lookupClassDefinition(data); FieldDefinition fd = portableContext.getFieldDefinition(classDefinition, "child"); assertNotNull(fd); assertEquals(FieldType.PORTABLE, fd.getType()); fd = portableContext.getFieldDefinition(classDefinition, "child.child"); assertNotNull(fd); assertEquals(FieldType.PORTABLE, fd.getType()); fd = portableContext.getFieldDefinition(classDefinition, "child.child.timestamp"); assertNotNull(fd); assertEquals(FieldType.LONG, fd.getType()); } @Test public void testWriteRead_withNullPortableArray() { int portableVersion = 1; ClassDefinitionBuilder builder0 = new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, 1, portableVersion); ClassDefinitionBuilder builder1 = new ClassDefinitionBuilder(PORTABLE_FACTORY_ID, 2, portableVersion); builder0.addPortableArrayField("list", builder1.build()); SerializationService ss = new DefaultSerializationServiceBuilder() .setPortableVersion(portableVersion) .addClassDefinition(builder0.build()) .addClassDefinition(builder1.build()) .build(); Data data = ss.toData(new TestObject1()); SerializationService ss2 = new DefaultSerializationServiceBuilder() .addPortableFactory(1, new PortableFactory() { @Override public Portable create(int classId) { switch (classId) { case 1: return new TestObject1(); case 2: return new TestObject2(); } return null; } }) .build(); Object object = ss2.toObject(data); assertNotNull(object); assertTrue(object instanceof TestObject1); } public static class GrandParentPortableObject implements Portable { long timestamp; ParentPortableObject child; public GrandParentPortableObject(long timestamp) { this.timestamp = timestamp; child = new ParentPortableObject(timestamp); } public GrandParentPortableObject(long timestamp, ParentPortableObject child) { this.timestamp = timestamp; this.child = child; } @Override public int getFactoryId() { return 1; } @Override public int getClassId() { return 1; } @Override public void readPortable(PortableReader reader) throws IOException { timestamp = reader.readLong("timestamp"); child = reader.readPortable("child"); } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeLong("timestamp", timestamp); writer.writePortable("child", child); } } public static class ParentPortableObject implements Portable { long timestamp; ChildPortableObject child; public ParentPortableObject(long timestamp) { this.timestamp = timestamp; child = new ChildPortableObject(timestamp); } public ParentPortableObject(long timestamp, ChildPortableObject child) { this.timestamp = timestamp; this.child = child; } @Override public int getFactoryId() { return 2; } @Override public int getClassId() { return 2; } @Override public void readPortable(PortableReader reader) throws IOException { timestamp = reader.readLong("timestamp"); child = reader.readPortable("child"); } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeLong("timestamp", timestamp); writer.writePortable("child", child); } } public static class ChildPortableObject implements Portable { long timestamp; public ChildPortableObject(long timestamp) { this.timestamp = timestamp; } @Override public int getFactoryId() { return 3; } @Override public int getClassId() { return 3; } @Override public void readPortable(PortableReader reader) throws IOException { timestamp = reader.readLong("timestamp"); } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeLong("timestamp", timestamp); } } static class TestObject1 implements Portable { private Portable[] portables; public TestObject1() { } public TestObject1(Portable[] p) { portables = p; } @Override public int getFactoryId() { return PORTABLE_FACTORY_ID; } @Override public int getClassId() { return 1; } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writePortableArray("list", portables); } @Override public void readPortable(PortableReader reader) throws IOException { portables = reader.readPortableArray("list"); } } static class TestObject2 implements Portable { private String shortString; public TestObject2() { shortString = "Hello World"; } @Override public int getFactoryId() { return PORTABLE_FACTORY_ID; } @Override public int getClassId() { return 2; } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeUTF("shortString", shortString); } @Override public void readPortable(PortableReader reader) throws IOException { throw new UnsupportedOperationException(); } } public static class TestPortableFactory implements PortableFactory { @Override public Portable create(int classId) { switch (classId) { case TestSerializationConstants.MAIN_PORTABLE: return new MainPortable(); case TestSerializationConstants.INNER_PORTABLE: return new InnerPortable(); case TestSerializationConstants.NAMED_PORTABLE: return new NamedPortable(); case TestSerializationConstants.RAW_DATA_PORTABLE: return new RawDataPortable(); case TestSerializationConstants.INVALID_RAW_DATA_PORTABLE: return new InvalidRawDataPortable(); case TestSerializationConstants.INVALID_RAW_DATA_PORTABLE_2: return new InvalidRawDataPortable2(); case TestSerializationConstants.OBJECT_CARRYING_PORTABLE: return new ObjectCarryingPortable(); case TestSerializationConstants.ALL_FIELD_OBJECT_PORTABLE: return new SerializationV1Portable(); } return null; } } public static class TestDataSerializableFactory implements DataSerializableFactory { @Override public IdentifiedDataSerializable create(int typeId) { switch (typeId) { case TestSerializationConstants.SAMPLE_IDENTIFIED_DATA_SERIALIZABLE: return new SampleIdentifiedDataSerializable(); } return null; } } @Test public void testWriteObject_withPortable() { SerializationService ss = new DefaultSerializationServiceBuilder() .addPortableFactory(PORTABLE_FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new NamedPortableV2(); } }) .build(); SerializationService ss2 = new DefaultSerializationServiceBuilder() .addPortableFactory(PORTABLE_FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new NamedPortable(); } }) .setPortableVersion(5) .build(); Object o1 = new ComplexDataSerializable(new NamedPortableV2("test", 123, 500), new ByteArrayDataSerializable(new byte[3]), null); Data data = ss.toData(o1); Object o2 = ss2.toObject(data); assertEquals(o1, o2); } @Test public void testWriteData_withPortable() { SerializationService ss = new DefaultSerializationServiceBuilder() .addPortableFactory(PORTABLE_FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new NamedPortableV2(); } }) .build(); SerializationService ss2 = new DefaultSerializationServiceBuilder() .addPortableFactory(PORTABLE_FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new NamedPortable(); } }) .setPortableVersion(5) .build(); Portable p1 = new NamedPortableV2("test", 456, 500); Object o1 = new DataDataSerializable(ss.toData(p1)); Data data = ss.toData(o1); DataDataSerializable o2 = ss2.toObject(data); assertEquals(o1, o2); Portable p2 = ss2.toObject(o2.data); assertEquals(p1, p2); } @Test(expected = HazelcastSerializationException.class) public void testGenericPortable_whenMultipleTypesAreUsed() { SerializationService ss = new DefaultSerializationServiceBuilder() .addPortableFactory(1, new PortableFactory() { @Override public Portable create(int classId) { switch (classId) { case ParentGenericPortable.CLASS_ID: return new ParentGenericPortable(); case ChildGenericPortable1.CLASS_ID: return new ChildGenericPortable1(); case ChildGenericPortable2.CLASS_ID: return new ChildGenericPortable2(); } throw new IllegalArgumentException(); } }).build(); ss.toData(new ParentGenericPortable<ChildGenericPortable1>(new ChildGenericPortable1("aaa", "bbb"))); Data data = ss.toData(new ParentGenericPortable<ChildGenericPortable2>(new ChildGenericPortable2("ccc"))); ss.toObject(data); } @Test public void testWriteObjectWithPortable() { SerializationService serializationService = createSerializationService(1); NamedPortable namedPortable = new NamedPortable("name", 2); ObjectCarryingPortable objectCarryingPortable1 = new ObjectCarryingPortable(namedPortable); Data data = serializationService.toData(objectCarryingPortable1); ObjectCarryingPortable objectCarryingPortable2 = serializationService.toObject(data); assertEquals(objectCarryingPortable1, objectCarryingPortable2); } @Test public void testWriteObjectWithIdentifiedDataSerializable() { SerializationService serializationService = createSerializationService(1); SampleIdentifiedDataSerializable namedPortable = new SampleIdentifiedDataSerializable('c', 2); ObjectCarryingPortable objectCarryingPortable1 = new ObjectCarryingPortable(namedPortable); Data data = serializationService.toData(objectCarryingPortable1); ObjectCarryingPortable objectCarryingPortable2 = serializationService.toObject(data); assertEquals(objectCarryingPortable1, objectCarryingPortable2); } @Test public void testWriteObjectWithCustomSerializable() { SerializationConfig config = new SerializationConfig(); SerializerConfig sc = new SerializerConfig() .setImplementation(new CustomSerializationTest.FooXmlSerializer()) .setTypeClass(CustomSerializationTest.Foo.class); config.addSerializerConfig(sc); SerializationService serializationService = new DefaultSerializationServiceBuilder().setPortableVersion(1) .addPortableFactory(PORTABLE_FACTORY_ID, new TestPortableFactory()).setConfig(config).build(); CustomSerializationTest.Foo foo = new CustomSerializationTest.Foo("f"); ObjectCarryingPortable objectCarryingPortable1 = new ObjectCarryingPortable(foo); Data data = serializationService.toData(objectCarryingPortable1); ObjectCarryingPortable objectCarryingPortable2 = serializationService.toObject(data); assertEquals(objectCarryingPortable1, objectCarryingPortable2); } static class ParentGenericPortable<T extends Portable> implements Portable { static final int CLASS_ID = 1; T child; ParentGenericPortable() { } ParentGenericPortable(T child) { this.child = child; } @Override public int getFactoryId() { return 1; } @Override public int getClassId() { return CLASS_ID; } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writePortable("c", child); } @Override public void readPortable(PortableReader reader) throws IOException { child = reader.readPortable("c"); } } static class ChildGenericPortable1 implements Portable { static final int CLASS_ID = 2; String s1; String s2; ChildGenericPortable1() { } ChildGenericPortable1(String s1, String s2) { this.s1 = s1; this.s2 = s2; } @Override public int getFactoryId() { return 1; } @Override public int getClassId() { return CLASS_ID; } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeUTF("s1", s1); writer.writeUTF("s2", s2); } @Override public void readPortable(PortableReader reader) throws IOException { s1 = reader.readUTF("s1"); s2 = reader.readUTF("s2"); } } static class ChildGenericPortable2 implements Portable { static final int CLASS_ID = 3; String s; ChildGenericPortable2() { } ChildGenericPortable2(String s1) { this.s = s1; } @Override public int getFactoryId() { return 1; } @Override public int getClassId() { return CLASS_ID; } @Override public void writePortable(PortableWriter writer) throws IOException { writer.writeUTF("s", s); } @Override public void readPortable(PortableReader reader) throws IOException { s = reader.readUTF("s"); } } }