/* * 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.internal.serialization.impl; import com.hazelcast.core.HazelcastInstanceNotActiveException; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.nio.BufferObjectDataInput; import com.hazelcast.nio.BufferObjectDataOutput; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.hazelcast.nio.serialization.CustomSerializationTest; import com.hazelcast.nio.serialization.Data; import com.hazelcast.nio.serialization.DataSerializable; import com.hazelcast.nio.serialization.HazelcastSerializationException; import com.hazelcast.nio.serialization.StreamSerializer; import com.hazelcast.nio.serialization.TypedDataSerializable; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.hamcrest.core.Is; import org.hamcrest.core.IsInstanceOf; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class AbstractSerializationServiceTest { private AbstractSerializationService abstractSerializationService; @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setup() { DefaultSerializationServiceBuilder defaultSerializationServiceBuilder = new DefaultSerializationServiceBuilder(); abstractSerializationService = defaultSerializationServiceBuilder .setVersion(InternalSerializationService.VERSION_1).build(); } @Test public void toBytes_withPadding() { String payload = "somepayload"; int padding = 10; byte[] unpadded = abstractSerializationService.toBytes(payload); byte[] padded = abstractSerializationService.toBytes(payload, 10, true); // make sure the size is expected assertEquals(unpadded.length + padding, padded.length); // check if the actual content is the same for (int k = 0; k < unpadded.length; k++) { assertEquals(unpadded[k], padded[k + padding]); } } @Test public void testExternalizable() { ExternalizableValue original = new ExternalizableValue(100); Data data = abstractSerializationService.toData(original); ExternalizableValue found = abstractSerializationService.toObject(data); assertNotNull(found); assertEquals(original.value, found.value); } static class ExternalizableValue implements Externalizable { int value; ExternalizableValue() { } ExternalizableValue(int value) { this.value = value; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(value); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { value = in.readInt(); } } @Test public void testSerializable() { SerializableleValue original = new SerializableleValue(100); Data data = abstractSerializationService.toData(original); SerializableleValue found = abstractSerializationService.toObject(data); assertNotNull(found); assertEquals(original.value, found.value); } static class SerializableleValue implements Serializable { int value; SerializableleValue(int value) { this.value = value; } } @Test(expected = HazelcastSerializationException.class) public void testToBytesHandleThrowable() throws Exception { abstractSerializationService.register(StringBuffer.class, new StringBufferSerializer(true)); abstractSerializationService.toBytes(new StringBuffer()); } @Test public void testToObject_ServiceInactive() throws Exception { expectedException.expect(HazelcastSerializationException.class); expectedException.expectCause(Is.is(IsInstanceOf.<Throwable>instanceOf(HazelcastInstanceNotActiveException.class))); abstractSerializationService.register(StringBuffer.class, new StringBufferSerializer(false)); Data data = abstractSerializationService.toData(new StringBuffer()); abstractSerializationService.dispose(); abstractSerializationService.toObject(data); } @Test(expected = HazelcastSerializationException.class) public void testWriteObject_serializerFail() throws Exception { abstractSerializationService.register(StringBuffer.class, new StringBufferSerializer(true)); BufferObjectDataOutput out = abstractSerializationService.createObjectDataOutput(); abstractSerializationService.writeObject(out, new StringBuffer()); } @Test public void testReadObject_ServiceInactive() throws Exception { expectedException.expect(HazelcastSerializationException.class); expectedException.expectCause(Is.is(IsInstanceOf.<Throwable>instanceOf(HazelcastInstanceNotActiveException.class))); abstractSerializationService.register(StringBuffer.class, new StringBufferSerializer(false)); Data data = abstractSerializationService.toData(new StringBuffer()); abstractSerializationService.dispose(); BufferObjectDataInput in = abstractSerializationService.createObjectDataInput(data); in.position(HeapData.TYPE_OFFSET); abstractSerializationService.readObject(in); } @Test(expected = IllegalArgumentException.class) public void testRegister_nullType() throws Exception { abstractSerializationService.register(null, new StringBufferSerializer(true)); } @Test(expected = IllegalArgumentException.class) public void testRegister_typeIdNegative() throws Exception { StringBufferSerializer serializer = new StringBufferSerializer(true); serializer.typeId = -10000; abstractSerializationService.register(StringBuffer.class, serializer); } @Test(expected = IllegalStateException.class) public void testGlobalRegister_doubleRegistration() throws Exception { abstractSerializationService.registerGlobal(new StringBufferSerializer(true)); abstractSerializationService.registerGlobal(new StringBufferSerializer(true)); } @Test(expected = IllegalStateException.class) public void testGlobalRegister_alreadyRegisteredType() throws Exception { abstractSerializationService.register(StringBuffer.class, new StringBufferSerializer(true)); abstractSerializationService.registerGlobal(new TheOtherGlobalSerializer(true)); } @Test(expected = IllegalArgumentException.class) public void testSafeRegister_ConstantType() throws Exception { abstractSerializationService.safeRegister(Integer.class, new StringBufferSerializer(true)); } @Test(expected = IllegalStateException.class) public void testSafeRegister_alreadyRegisteredType() throws Exception { abstractSerializationService.safeRegister(StringBuffer.class, new StringBufferSerializer(true)); abstractSerializationService.safeRegister(StringBuffer.class, new TheOtherGlobalSerializer(true)); } @Test(expected = IllegalStateException.class) public void testSafeRegister_alreadyRegisteredTypeId() throws Exception { abstractSerializationService.safeRegister(StringBuffer.class, new StringBufferSerializer(true)); abstractSerializationService.safeRegister(StringBuilder.class, new TheOtherGlobalSerializer(true)); } @Test(expected = HazelcastInstanceNotActiveException.class) public void testSerializerFor_ServiceInactive() throws Exception { abstractSerializationService.dispose(); abstractSerializationService.serializerFor(new CustomSerializationTest.Foo()); } @Test public void testDeserializationForSpecificType() { BaseClass baseObject = new BaseClass(5, "abc"); ExtendedClass extendedObject = new ExtendedClass(baseObject, 378); Data extendedData = abstractSerializationService.toData(extendedObject); Object deserializedObject = abstractSerializationService.toObject(extendedData); assertEquals(extendedObject, deserializedObject); deserializedObject = abstractSerializationService.toObject(extendedObject, BaseClass.class); assertEquals(baseObject, deserializedObject); } @Test public void testTypedSerialization() { BaseClass baseObject = new BaseClass(); Data data = abstractSerializationService.toData(baseObject); Object deserializedObject = abstractSerializationService.toObject(data); assertEquals(baseObject, deserializedObject); TypedBaseClass typedBaseObject = new TypedBaseClass(baseObject); Data typedData = abstractSerializationService.toData(typedBaseObject); deserializedObject = abstractSerializationService.toObject(typedData, TypedBaseClass.class); assertEquals(typedBaseObject, deserializedObject); } public static class TypedBaseClass implements DataSerializable, TypedDataSerializable { private final BaseClass innerObj; public TypedBaseClass() { innerObj = new BaseClass(); } public TypedBaseClass(BaseClass innerObj) { this.innerObj = innerObj; } @Override public Class getClassType() { return BaseClass.class; } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeInt(innerObj.intField); } @Override public void readData(ObjectDataInput in) throws IOException { innerObj.intField = in.readInt(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (null == obj) { return false; } if (null == obj || !(getClass().isAssignableFrom(obj.getClass()))) { return false; } TypedBaseClass rhs = (TypedBaseClass) obj; if (null == innerObj && null != rhs.innerObj || null != innerObj && null == rhs.innerObj) { return false; } if (null == innerObj && null == rhs.innerObj) { return true; } return innerObj.equals(rhs.innerObj); } } public static class BaseClass implements DataSerializable { private int intField; private String stringField; public BaseClass() { } public BaseClass(BaseClass rhs) { this.intField = rhs.intField; this.stringField = rhs.stringField; } public BaseClass(int intField, String stringField) { this.intField = intField; this.stringField = stringField; } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeInt(intField); out.writeUTF(stringField); } @Override public void readData(ObjectDataInput in) throws IOException { intField = in.readInt(); stringField = in.readUTF(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || !getClass().isAssignableFrom(o.getClass())) { return false; } BaseClass baseClass = (BaseClass) o; if (intField != baseClass.intField) { return false; } return stringField != null ? stringField.equals(baseClass.stringField) : baseClass.stringField == null; } @Override public int hashCode() { int result = intField; result = 31 * result + (stringField != null ? stringField.hashCode() : 0); return result; } } public static class ExtendedClass extends BaseClass { private long longField; public ExtendedClass() { } public ExtendedClass(BaseClass baseObject, long longField) { super(baseObject); this.longField = longField; } @Override public void writeData(ObjectDataOutput out) throws IOException { super.writeData(out); out.writeLong(longField); } @Override public void readData(ObjectDataInput in) throws IOException { super.readData(in); longField = in.readLong(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || !getClass().isAssignableFrom(o.getClass())) { return false; } if (!super.equals(o)) { return false; } ExtendedClass that = (ExtendedClass) o; return longField == that.longField; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (int) (longField ^ (longField >>> 32)); return result; } } private class StringBufferSerializer implements StreamSerializer<StringBuffer> { int typeId = 100000; private boolean fail; public StringBufferSerializer(boolean fail) { this.fail = fail; } @Override public int getTypeId() { return typeId; } @Override public void destroy() { } @Override public void write(ObjectDataOutput out, StringBuffer stringBuffer) throws IOException { if (fail) { throw new RuntimeException(); } else { out.writeUTF(stringBuffer.toString()); } } @Override public StringBuffer read(ObjectDataInput in) throws IOException { if (fail) { throw new RuntimeException(); } else { return new StringBuffer(in.readUTF()); } } } private class TheOtherGlobalSerializer extends StringBufferSerializer { public TheOtherGlobalSerializer(boolean fail) { super(fail); } } }