blob: 0aad9546c1e0dae23744443b91e65ff8b34fd85b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.pdx;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.DataSerializer;
import org.apache.geode.SerializationException;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.internal.HeapDataOutputStream;
import org.apache.geode.internal.PdxSerializerObject;
import org.apache.geode.internal.Version;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.pdx.internal.AutoSerializableManager;
import org.apache.geode.pdx.internal.PdxField;
import org.apache.geode.pdx.internal.PdxInstanceImpl;
import org.apache.geode.test.junit.categories.SerializationTest;
@Category({SerializationTest.class})
public class AutoSerializableJUnitTest {
private GemFireCacheImpl c;
private AutoSerializableManager manager;
private ReflectionBasedAutoSerializer serializer;
private String[] stdSerializableClasses = new String[] {"org.apache.geode.pdx.DomainObject.*"};
public AutoSerializableJUnitTest() {
super();
}
@Before
public void setUp() throws Exception {
System.setProperty(AutoSerializableManager.NO_HARDCODED_EXCLUDES_PARAM, "true");
}
@After
public void tearDown() {
c.close();
}
private void setupSerializer(String... classPatterns) {
setupSerializer(new ReflectionBasedAutoSerializer(classPatterns), true);
}
private void setupSerializer(boolean checkPortability, String... classPatterns) {
setupSerializer(new ReflectionBasedAutoSerializer(checkPortability, classPatterns), true);
}
private void setupSerializer(boolean checkPortability, boolean readSerialized,
String... classPatterns) {
setupSerializer(new ReflectionBasedAutoSerializer(checkPortability, classPatterns),
readSerialized);
}
private void setupSerializer(ReflectionBasedAutoSerializer s, boolean readSerialized) {
this.serializer = s;
this.manager = (AutoSerializableManager) s.getManager();
this.c = (GemFireCacheImpl) new CacheFactory().set(MCAST_PORT, "0")
.setPdxReadSerialized(readSerialized).setPdxSerializer(s).create();
}
/*
* Test basic functionality.
*/
@Test
public void testSanity() throws Exception {
setupSerializer("org.apache.geode.pdx.DomainObjectPdxAuto");
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("string_0", "test string value");
objOut.set("long_0", 99L);
objOut.set("string_immediate", "right now");
objOut.set("anEnum", DomainObjectPdxAuto.Day.FRIDAY);
List<String> list = new ArrayList<String>(4);
list.add("string one");
list.add("string two");
list.add("string three");
list.add("string four");
objOut.set("string_list", list);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(99L, pdxIn.getField("long_0"));
assertEquals("test string value", pdxIn.getField("string_0"));
assertEquals("right now", pdxIn.getField("string_immediate"));
{
PdxInstance epi = (PdxInstance) pdxIn.getField("anEnum");
assertEquals(true, epi.isEnum());
assertEquals(true, epi.hasField("name"));
assertEquals(true, epi.hasField("ordinal"));
assertEquals("org.apache.geode.pdx.DomainObjectPdxAuto$Day", epi.getClassName());
assertEquals("FRIDAY", epi.getField("name"));
assertEquals(DomainObjectPdxAuto.Day.FRIDAY.ordinal(), epi.getField("ordinal"));
assertEquals(DomainObjectPdxAuto.Day.FRIDAY, epi.getObject());
}
assertEquals(4, ((List) pdxIn.getField("string_list")).size());
{
DomainObjectPdxAuto result = (DomainObjectPdxAuto) pdxIn.getObject();
assertEquals(99L, result.get("long_0"));
assertEquals("test string value", result.get("string_0"));
assertEquals("right now", result.get("string_immediate"));
assertEquals(DomainObjectPdxAuto.Day.FRIDAY, result.get("anEnum"));
assertEquals(4, ((List) result.get("string_list")).size());
}
// disable pdx instances to make sure we can deserialize without calling PdxInstance.getObject
PdxInstanceImpl.setPdxReadSerialized(false);
try {
DomainObjectPdxAuto result = (DomainObjectPdxAuto) DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(99L, result.get("long_0"));
assertEquals("test string value", result.get("string_0"));
assertEquals("right now", result.get("string_immediate"));
assertEquals(DomainObjectPdxAuto.Day.FRIDAY, result.get("anEnum"));
assertEquals(4, ((List) result.get("string_list")).size());
} finally {
PdxInstanceImpl.setPdxReadSerialized(true);
}
}
@Test
public void testConcurrentHashMap() throws Exception {
setupSerializer("java.util.concurrent..*");
ConcurrentHashMap<String, String> m = new ConcurrentHashMap<String, String>();
m.put("k1", "v1");
m.put("k2", "v2");
m.put("k3", "v3");
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(m, out);
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(m, dObj);
}
@Test
public void testMonth() throws Exception {
setupSerializer(false, false, "org.apache.geode.pdx.AutoSerializableJUnitTest.MyMonth");
MyMonth m = new MyMonth(1);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(m, out);
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(m, dObj);
}
// This class can only be serialized by the auto serializer
// if the unsafe code is available.
// If unsafe is not available it automatically ignores this class
// since it does not have a public no-arg constructor
public static class MyMonth implements Serializable, PdxSerializerObject {
private int month;
MyMonth(int month) {
this.month = month;
}
public int hashCode() {
return month;
}
public boolean equals(Object obj) {
if (obj instanceof MyMonth) {
if (this.month == ((MyMonth) obj).month) {
return true;
} else {
return false;
}
} else {
return false;
}
}
}
public static class MyExternalizable implements Externalizable, PdxSerializerObject {
private int v;
public MyExternalizable() {}
public MyExternalizable(int v) {
this.v = v;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(v);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.v = in.readInt();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + v;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyExternalizable other = (MyExternalizable) obj;
if (v != other.v)
return false;
return true;
}
}
@Test
public void testExternalizable() throws Exception {
setupSerializer("org.apache.geode.pdx.AutoSerializableJUnitTest.MyExternalizable");
MyExternalizable o = new MyExternalizable(79);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(o, out);
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(o, dObj);
}
public static class MyWriteReplace implements Serializable, PdxSerializerObject {
private String v;
public MyWriteReplace(String v) {
this.v = v;
}
private Object writeReplace() throws ObjectStreamException {
return v;
}
}
/**
* A serializable with a writeReplace method should use standard serialization.
*/
@Test
public void testWriteReplace() throws Exception {
setupSerializer("org.apache.geode.pdx.AutoSerializableJUnitTest.MyWriteReplace");
MyWriteReplace o = new MyWriteReplace("79");
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(o, out);
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals("79", dObj);
}
public static class MyComparator implements Comparator, PdxSerializerObject {
public MyComparator() {}
@Override
public int compare(Object o1, Object o2) {
return 0;
}
}
/**
* A serializable with a writeReplace method should use standard serialization.
*/
@Test
public void testComparator() throws Exception {
setupSerializer("org.apache.geode.pdx.AutoSerializableJUnitTest.MyComparator");
TreeSet o = new TreeSet(new MyComparator());
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(o, out);
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(o, dObj);
}
public static class PrimitiveObjectHolder implements PdxSerializerObject, Serializable {
public Boolean bool;
public Byte b;
public Character c;
public Short s;
public Integer i;
public Long l;
public Float f;
public Double d;
public PrimitiveObjectHolder() {}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((b == null) ? 0 : b.hashCode());
result = prime * result + ((bool == null) ? 0 : bool.hashCode());
result = prime * result + ((c == null) ? 0 : c.hashCode());
result = prime * result + ((d == null) ? 0 : d.hashCode());
result = prime * result + ((f == null) ? 0 : f.hashCode());
result = prime * result + ((i == null) ? 0 : i.hashCode());
result = prime * result + ((l == null) ? 0 : l.hashCode());
result = prime * result + ((s == null) ? 0 : s.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PrimitiveObjectHolder other = (PrimitiveObjectHolder) obj;
if (b == null) {
if (other.b != null)
return false;
} else if (!b.equals(other.b))
return false;
if (bool == null) {
if (other.bool != null)
return false;
} else if (!bool.equals(other.bool))
return false;
if (c == null) {
if (other.c != null)
return false;
} else if (!c.equals(other.c))
return false;
if (d == null) {
if (other.d != null)
return false;
} else if (!d.equals(other.d))
return false;
if (f == null) {
if (other.f != null)
return false;
} else if (!f.equals(other.f))
return false;
if (i == null) {
if (other.i != null)
return false;
} else if (!i.equals(other.i))
return false;
if (l == null) {
if (other.l != null)
return false;
} else if (!l.equals(other.l))
return false;
if (s == null) {
if (other.s != null)
return false;
} else if (!s.equals(other.s))
return false;
return true;
}
}
public static class BigHolder implements PdxSerializerObject, Serializable {
private BigInteger bi;
private BigDecimal bd;
public BigHolder() {}
public BigHolder(BigInteger bi, BigDecimal bd) {
this.bi = bi;
this.bd = bd;
}
public BigHolder(int i) {
this.bi = new BigInteger("" + i);
this.bd = new BigDecimal("" + i);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bd == null) ? 0 : bd.hashCode());
result = prime * result + ((bi == null) ? 0 : bi.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BigHolder other = (BigHolder) obj;
if (bd == null) {
if (other.bd != null)
return false;
} else if (!bd.equals(other.bd))
return false;
if (bi == null) {
if (other.bi != null)
return false;
} else if (!bi.equals(other.bi))
return false;
return true;
}
}
public static class CHMHolder implements PdxSerializerObject {
private ConcurrentHashMap chm;
public CHMHolder() {}
public CHMHolder(ConcurrentHashMap chm) {
this.chm = chm;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((chm == null) ? 0 : chm.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CHMHolder other = (CHMHolder) obj;
if (chm == null) {
if (other.chm != null)
return false;
} else if (!chm.equals(other.chm))
return false;
return true;
}
}
@Test
public void testCheckPortablity() throws Exception {
setupSerializer(true, "org.apache.geode.pdx.AutoSerializableJUnitTest.BigHolder");
BigInteger bi = new BigInteger("12345678901234567890");
BigDecimal bd = new BigDecimal("1234567890.1234567890");
BigHolder bih = new BigHolder(bi, bd);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
try {
DataSerializer.writeObject(bih, out);
throw new RuntimeException("expected NonPortableClassException");
} catch (NonPortableClassException expected) {
}
}
public static class ExplicitClassNameAutoSerializer extends ReflectionBasedAutoSerializer {
public ExplicitClassNameAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public boolean isClassAutoSerialized(Class<?> clazz) {
return DomainObjectPdxAuto.class.equals(clazz);
}
}
@Test
public void testIsClassAutoSerialized() throws IOException, ClassNotFoundException {
setupSerializer(new ExplicitClassNameAutoSerializer(false, ""), false);
DomainObject objOut = new DomainObjectPdxAuto(4);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
DomainObject objIn = (DomainObject) DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(objOut, objIn);
}
public static class ExplicitIdentityAutoSerializer extends ReflectionBasedAutoSerializer {
public ExplicitIdentityAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public boolean isIdentityField(Field f, Class<?> clazz) {
return f.getName().equals("long_0");
}
}
@Test
public void testIsIdentityField() throws IOException, ClassNotFoundException {
setupSerializer(
new ExplicitIdentityAutoSerializer(false, "org.apache.geode.pdx.DomainObjectPdxAuto"),
true);
DomainObject objOut = new DomainObjectPdxAuto(4);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
DataSerializer.writeObject(objOut, out);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
PdxInstance pi = (PdxInstance) DataSerializer.readObject(dis);
PdxInstance pi2 = (PdxInstance) DataSerializer.readObject(dis);
assertEquals(true, pi.isIdentityField("long_0"));
assertEquals(false, pi.isIdentityField("string_0"));
assertEquals(false, pi.isEnum());
assertEquals(objOut.getClass().getName(), pi.getClassName());
assertEquals(objOut, pi.getObject());
assertEquals(objOut, pi2.getObject());
}
public static class ExplicitIncludedAutoSerializer extends ReflectionBasedAutoSerializer {
public ExplicitIncludedAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public boolean isFieldIncluded(Field f, Class<?> clazz) {
return f.getName().equals("long_0");
}
}
@Test
public void testIsFieldIncluded() throws IOException, ClassNotFoundException {
setupSerializer(
new ExplicitIncludedAutoSerializer(false, "org.apache.geode.pdx.DomainObjectPdxAuto"),
true);
DomainObject objOut = new DomainObjectPdxAuto(4);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pi = (PdxInstance) DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(true, pi.hasField("long_0"));
assertEquals(false, pi.hasField("string_0"));
}
public static class ExplicitFieldNameAutoSerializer extends ReflectionBasedAutoSerializer {
public ExplicitFieldNameAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public String getFieldName(Field f, Class<?> clazz) {
if (f.getName().equals("long_0")) {
return "_long_0";
} else {
return super.getFieldName(f, clazz);
}
}
}
@Test
public void testGetFieldName() throws IOException, ClassNotFoundException {
setupSerializer(
new ExplicitFieldNameAutoSerializer(false, "org.apache.geode.pdx.DomainObjectPdxAuto"),
true);
DomainObject objOut = new DomainObjectPdxAuto(4);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pi = (PdxInstance) DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
System.out.println("fieldNames=" + pi.getFieldNames());
assertEquals(false, pi.hasField("long_0"));
assertEquals(true, pi.hasField("_long_0"));
assertEquals(true, pi.hasField("string_0"));
assertEquals(objOut, pi.getObject());
}
public static class BigIntegerAutoSerializer extends ReflectionBasedAutoSerializer {
public BigIntegerAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public FieldType getFieldType(Field f, Class<?> clazz) {
if (f.getType().equals(BigInteger.class)) {
return FieldType.BYTE_ARRAY;
} else if (f.getType().equals(BigDecimal.class)) {
return FieldType.STRING;
} else {
return super.getFieldType(f, clazz);
}
}
@Override
public boolean transformFieldValue(Field f, Class<?> clazz) {
if (f.getType().equals(BigInteger.class)) {
return true;
} else if (f.getType().equals(BigDecimal.class)) {
return true;
} else {
return super.transformFieldValue(f, clazz);
}
}
@Override
public Object writeTransform(Field f, Class<?> clazz, Object originalValue) {
if (f.getType().equals(BigInteger.class)) {
byte[] result = null;
if (originalValue != null) {
BigInteger bi = (BigInteger) originalValue;
result = bi.toByteArray();
}
return result;
} else if (f.getType().equals(BigDecimal.class)) {
Object result = null;
if (originalValue != null) {
BigDecimal bd = (BigDecimal) originalValue;
result = bd.toString();
}
return result;
} else {
return super.writeTransform(f, clazz, originalValue);
}
}
@Override
public Object readTransform(Field f, Class<?> clazz, Object serializedValue) {
if (f.getType().equals(BigInteger.class)) {
BigInteger result = null;
if (serializedValue != null) {
result = new BigInteger((byte[]) serializedValue);
}
return result;
} else if (f.getType().equals(BigDecimal.class)) {
BigDecimal result = null;
if (serializedValue != null) {
result = new BigDecimal((String) serializedValue);
}
return result;
} else {
return super.readTransform(f, clazz, serializedValue);
}
}
}
public static class PrimitiveObjectsAutoSerializer extends ReflectionBasedAutoSerializer {
public PrimitiveObjectsAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public FieldType getFieldType(Field f, Class<?> clazz) {
if (f.getType().equals(Boolean.class)) {
return FieldType.BOOLEAN;
} else if (f.getType().equals(Byte.class)) {
return FieldType.BYTE;
} else if (f.getType().equals(Character.class)) {
return FieldType.CHAR;
} else if (f.getType().equals(Short.class)) {
return FieldType.SHORT;
} else if (f.getType().equals(Integer.class)) {
return FieldType.INT;
} else if (f.getType().equals(Long.class)) {
return FieldType.LONG;
} else if (f.getType().equals(Float.class)) {
return FieldType.FLOAT;
} else if (f.getType().equals(Double.class)) {
return FieldType.DOUBLE;
} else {
return super.getFieldType(f, clazz);
}
}
@Override
public boolean transformFieldValue(Field f, Class<?> clazz) {
if (f.getType().equals(Boolean.class)) {
return true;
} else if (f.getType().equals(Byte.class)) {
return true;
} else if (f.getType().equals(Character.class)) {
return true;
} else if (f.getType().equals(Short.class)) {
return true;
} else if (f.getType().equals(Integer.class)) {
return true;
} else if (f.getType().equals(Long.class)) {
return true;
} else if (f.getType().equals(Float.class)) {
return true;
} else if (f.getType().equals(Double.class)) {
return true;
} else {
return super.transformFieldValue(f, clazz);
}
}
@Override
public Object writeTransform(Field f, Class<?> clazz, Object originalValue) {
if (f.getType().equals(Boolean.class)) {
if (originalValue == null) {
return false;
} else {
return originalValue;
}
} else if (f.getType().equals(Byte.class)) {
if (originalValue == null) {
return (byte) 0;
} else {
return originalValue;
}
} else if (f.getType().equals(Character.class)) {
if (originalValue == null) {
return (char) 0;
} else {
return originalValue;
}
} else if (f.getType().equals(Short.class)) {
if (originalValue == null) {
return (short) 0;
} else {
return originalValue;
}
} else if (f.getType().equals(Integer.class)) {
if (originalValue == null) {
return (int) 0;
} else {
return originalValue;
}
} else if (f.getType().equals(Long.class)) {
if (originalValue == null) {
return 0L;
} else {
return originalValue;
}
} else if (f.getType().equals(Float.class)) {
if (originalValue == null) {
return (float) 0;
} else {
return originalValue;
}
} else if (f.getType().equals(Double.class)) {
if (originalValue == null) {
return (double) 0;
} else {
return originalValue;
}
} else {
return super.writeTransform(f, clazz, originalValue);
}
}
@Override
public Object readTransform(Field f, Class<?> clazz, Object serializedValue) {
if (f.getType().equals(Boolean.class)) {
return serializedValue;
} else if (f.getType().equals(Byte.class)) {
return serializedValue;
} else if (f.getType().equals(Character.class)) {
return serializedValue;
} else if (f.getType().equals(Short.class)) {
return serializedValue;
} else if (f.getType().equals(Integer.class)) {
return serializedValue;
} else if (f.getType().equals(Long.class)) {
return serializedValue;
} else if (f.getType().equals(Float.class)) {
return serializedValue;
} else if (f.getType().equals(Double.class)) {
return serializedValue;
} else {
return super.readTransform(f, clazz, serializedValue);
}
}
}
@Test
public void testPrimitiveObjects() throws Exception {
setupSerializer(new PrimitiveObjectsAutoSerializer(true,
"org.apache.geode.pdx.AutoSerializableJUnitTest.PrimitiveObjectHolder"), true);
PrimitiveObjectHolder nullHolder = new PrimitiveObjectHolder();
PrimitiveObjectHolder defaultHolder = new PrimitiveObjectHolder();
defaultHolder.bool = false;
defaultHolder.b = 0;
defaultHolder.c = 0;
defaultHolder.s = 0;
defaultHolder.i = 0;
defaultHolder.l = 0L;
defaultHolder.f = 0.0f;
defaultHolder.d = 0.0;
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(nullHolder, out);
PdxInstance pi =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
PdxField pf = ((PdxInstanceImpl) pi).getPdxField("f");
assertEquals(FieldType.FLOAT, pf.getFieldType());
Object dObj = pi.getObject();
assertFalse(nullHolder.equals(dObj));
assertEquals(defaultHolder, dObj);
out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(defaultHolder, out);
pi = DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
dObj = pi.getObject();
assertEquals(defaultHolder, dObj);
}
@Test
public void testExtensibility() throws Exception {
setupSerializer(new BigIntegerAutoSerializer(true,
"org.apache.geode.pdx.AutoSerializableJUnitTest.BigHolder"), false);
doExtensible("with autoSerializer handling Big*");
}
@Test
public void testNoExtensibility() throws Exception {
setupSerializer(false, false, "org.apache.geode.pdx.AutoSerializableJUnitTest.BigHolder");
doExtensible("using standard serialization of Big*");
}
private void doExtensible(String msg) throws IOException, ClassNotFoundException {
BigInteger bi = new BigInteger("12345678901234567890");
BigDecimal bd = new BigDecimal("1234567890.1234567890");
BigHolder bih = new BigHolder(bi, bd);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(bih, out);
System.out.println(msg + " out.size=" + out.size());
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(bih, dObj);
}
public static class ConcurrentHashMapAutoSerializer extends BigIntegerAutoSerializer {
public ConcurrentHashMapAutoSerializer(boolean checkPortability, String... patterns) {
super(checkPortability, patterns);
}
@Override
public FieldType getFieldType(Field f, Class<?> clazz) {
if (f.getType().equals(ConcurrentHashMap.class)) {
return FieldType.OBJECT_ARRAY;
} else {
return super.getFieldType(f, clazz);
}
}
@Override
public boolean transformFieldValue(Field f, Class<?> clazz) {
if (f.getType().equals(ConcurrentHashMap.class)) {
return true;
} else {
return super.transformFieldValue(f, clazz);
}
}
@Override
public Object writeTransform(Field f, Class<?> clazz, Object originalValue) {
if (f.getType().equals(ConcurrentHashMap.class)) {
Object[] result = null;
if (originalValue != null) {
ConcurrentHashMap<?, ?> m = (ConcurrentHashMap<?, ?>) originalValue;
result = new Object[m.size() * 2];
int i = 0;
for (Map.Entry<?, ?> e : m.entrySet()) {
result[i++] = e.getKey();
result[i++] = e.getValue();
}
}
return result;
} else {
return super.writeTransform(f, clazz, originalValue);
}
}
@Override
public Object readTransform(Field f, Class<?> clazz, Object serializedValue) {
if (f.getType().equals(ConcurrentHashMap.class)) {
ConcurrentHashMap result = null;
if (serializedValue != null) {
Object[] data = (Object[]) serializedValue;
result = new ConcurrentHashMap(data.length / 2);
int i = 0;
while (i < data.length) {
Object key = data[i++];
Object value = data[i++];
result.put(key, value);
}
}
return result;
} else {
return super.readTransform(f, clazz, serializedValue);
}
}
}
@Test
public void testCHM() throws Exception {
setupSerializer(new ConcurrentHashMapAutoSerializer(false,
"org.apache.geode.pdx.AutoSerializableJUnitTest.*Holder"), false);
doCHM("with autoSerializer handling ConcurrentHashMap");
}
@Test
public void testNoCHM() throws Exception {
setupSerializer(false, false, "org.apache.geode.pdx.AutoSerializableJUnitTest.*Holder");
doCHM("without autoSerializer handling ConcurrentHashMap");
}
private void doCHM(String msg) throws IOException, ClassNotFoundException {
ConcurrentHashMap<String, BigHolder> chm = new ConcurrentHashMap<String, BigHolder>();
for (int i = 1; i < 32; i++) {
chm.put("key" + i, new BigHolder(i));
}
CHMHolder h = new CHMHolder(chm);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(h, out);
System.out.println(msg + " out.size=" + out.size());
Object dObj =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(h, dObj);
}
/*
* Test the versioning of basic primitives where no fields are written at first, but then all
* fields need to be read back.
*/
@Test
public void testReadNullPrimitives() throws Exception {
setupSerializer(stdSerializableClasses);
// Don't want to write any fields
manager.addExcludePattern(".*DomainObjectPdxAuto",
"a(Char|Boolean|Byte|Short|Int|Long|Float|Double)");
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("aString", "aString has a value");
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
// Now we want to read all fields.
manager.resetCaches();
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
// Force the object to be de-serialized without any exceptions being thrown
DomainObjectPdxAuto result = (DomainObjectPdxAuto) pdxIn.getObject();
assertEquals('\u0000', result.aChar);
assertFalse(result.aBoolean);
assertEquals(0, result.aByte);
assertEquals(0, result.aShort);
assertEquals(0, result.anInt);
assertEquals(0L, result.aLong);
assertEquals(0.0f, result.aFloat, 0.0f);
assertEquals(0.0d, result.aDouble, 0.0f);
assertEquals("aString has a value", result.get("aString"));
}
/*
* Test that when primitive wrapper classes are null, and get serialized, they remain as null when
* being deserialized.
*/
@Test
public void testReadNullObjects() throws Exception {
setupSerializer(stdSerializableClasses);
// Don't want to write any fields
DomainObjectPdxAuto objOut = new DomainObjectPdxAuto(4);
objOut.anInteger = null;
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
// Now we want to read all fields.
manager.resetCaches();
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
DomainObjectPdxAuto result = (DomainObjectPdxAuto) pdxIn.getObject();
assertNull(result.anInteger);
assertNull(result.anEnum);
}
/*
* Test what happens with a class without a zero-arg constructor
*/
@Test
public void testNoZeroArgConstructor() throws Exception {
setupSerializer(stdSerializableClasses);
DomainObject objOut = new DomainObjectPdxAutoNoDefaultConstructor(4);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
try {
DataSerializer.writeObject(objOut, out);
} catch (NotSerializableException ex) {
// This passes the test
} catch (Exception ex) {
throw new RuntimeException("Expected NotSerializableException here but got " + ex);
}
}
/*
* Check that an exception is appropriately thrown for a 'bad' object. In this case the bad class
* masks a field from a superclass and defines it as a different type.
*/
@Test
public void testException() throws Exception {
setupSerializer(stdSerializableClasses);
DomainObject objOut = new DomainObjectBad();
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
try {
DataSerializer.writeObject(objOut, out);
} catch (SerializationException ex) {
// Pass
} catch (Exception ex) {
}
}
/*
* Test basic declarative configuration
*/
@Test
public void testBasicConfig() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes", "org.apache.geode.pdx.DomainObject");
serializer.initialize(null, props);
assertEquals(4, manager.getFields(DomainObject.class).size());
}
/*
* Test declarative configuration with excludes
*/
@Test
public void testConfigWithExclude1() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes", "org.apache.geode.pdx.DomainObject#exclude=long.*");
serializer.initialize(this.c, props);
assertEquals(3, manager.getFields(DomainObject.class).size());
}
@Test
public void testConfigWithExclude2() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes", "org.apache.geode.pdx.DomainObject#exclude=string.* ,");
serializer.initialize(this.c, props);
assertEquals(1, manager.getFields(DomainObject.class).size());
}
/*
* Test use of the identity param
*/
@Test
public void testConfigWithIdentity1() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes", "org.apache.geode.pdx.DomainObjectPdxAuto#identity=long.*");
serializer.initialize(this.c, props);
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("string_0", "test string value");
objOut.set("long_0", 99L);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
assertEquals(99L, pdxIn.getField("long_0"));
assertTrue(pdxIn.isIdentityField("long_0"));
}
/*
* Test both identity and exclude parameters
*/
@Test
public void testConfigWithIdentityAndExclude1() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes",
"org.apache.geode.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*");
serializer.initialize(this.c, props);
assertEquals(27, manager.getFields(DomainObjectPdxAuto.class).size());
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("string_0", "test string value");
objOut.set("long_0", 99L);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
// This means we're not doing anything with this string
assertNull(pdxIn.getField("string_0"));
assertTrue(pdxIn.isIdentityField("long_0"));
}
/*
* Variation of the config param
*/
@Test
public void testConfigWithIdentityAndExclude2() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes",
"org.apache.geode.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*#, com.another.class.Foo");
serializer.initialize(this.c, props);
assertEquals(27, manager.getFields(DomainObjectPdxAuto.class).size());
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("string_0", "test string value");
objOut.set("long_0", 99L);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
// This means we're not doing anything with this string
assertNull(pdxIn.getField("string_0"));
assertTrue(pdxIn.isIdentityField("long_0"));
}
/*
* Variation of the config param
*/
@Test
public void testConfigWithIdentityAndExclude3() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes",
"org.apache.geode.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*, com.another.class.Foo");
serializer.initialize(this.c, props);
assertEquals(27, manager.getFields(DomainObjectPdxAuto.class).size());
DomainObject objOut = new DomainObjectPdxAuto(4);
objOut.set("string_0", "test string value");
objOut.set("long_0", 99L);
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(objOut, out);
PdxInstance pdxIn =
DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
// This means we're not doing anything with this string
assertNull(pdxIn.getField("string_0"));
assertTrue(pdxIn.isIdentityField("long_0"));
}
/*
* Check repeat class definitions
*/
@Test
public void testConfigWithIdentityAndExclude4() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes", "org.apache.geode.pdx.DomainObjectPdxAuto#exclude=string.*, "
+ "org.apache.geode.pdx.DomainObjectPdxAuto#exclude=long.*");
serializer.initialize(this.c, props);
assertEquals(26, manager.getFields(DomainObjectPdxAuto.class).size());
}
/*
* Test correct config comes back via getConfig.
*/
@Test
public void testGetConfig() throws Exception {
setupSerializer();
Properties props = new Properties();
props.put("classes",
"Pdx#exclude=string.*#exclude=badField, Pdx#identity=id.*, PdxAuto#exclude=long.*#identity=id.*");
serializer.initialize(this.c, props);
Properties result = serializer.getConfig();
assertEquals(
"Pdx, PdxAuto, Pdx#identity=id.*, PdxAuto#identity=id.*, Pdx#exclude=string.*, Pdx#exclude=badField, PdxAuto#exclude=long.*",
result.getProperty("classes"));
manager.resetCaches();
serializer.initialize(this.c, result);
result = serializer.getConfig();
assertEquals(
"Pdx, PdxAuto, Pdx#identity=id.*, PdxAuto#identity=id.*, Pdx#exclude=string.*, Pdx#exclude=badField, PdxAuto#exclude=long.*",
result.getProperty("classes"));
}
/*
* Tests the exclusion algorithm to verify that it can be disabled.
*/
@Test
public void testNoHardCodedExcludes() {
System.setProperty(AutoSerializableManager.NO_HARDCODED_EXCLUDES_PARAM, "true");
setupSerializer();
assertFalse(manager.isExcluded("com.gemstone.gemfire.GemFireException"));
assertFalse(manager.isExcluded("com.gemstoneplussuffix.gemfire.GemFireException"));
assertFalse(manager.isExcluded("org.apache.geode.GemFireException"));
assertFalse(manager.isExcluded("org.apache.geodeplussuffix.gemfire.GemFireException"));
assertFalse(manager.isExcluded("javax.management.MBeanException"));
assertFalse(manager.isExcluded("javaxplussuffix.management.MBeanException"));
assertFalse(manager.isExcluded("java.lang.Exception"));
assertFalse(manager.isExcluded("javaplussuffix.lang.Exception"));
assertFalse(manager.isExcluded("com.example.Moof"));
}
/*
* Tests the exclusion algorithm to verify that it does not cast too wide of a net.
*/
@Test
public void testHardCodedExcludes() {
System.setProperty(AutoSerializableManager.NO_HARDCODED_EXCLUDES_PARAM, "false");
setupSerializer();
assertTrue(manager.isExcluded("com.gemstone.gemfire.GemFireException"));
assertFalse(manager.isExcluded("com.gemstoneplussuffix.gemfire.GemFireException"));
assertTrue(manager.isExcluded("org.apache.geode.GemFireException"));
assertFalse(manager.isExcluded("org.apache.geodeplussuffix.gemfire.GemFireException"));
assertTrue(manager.isExcluded("javax.management.MBeanException"));
assertFalse(manager.isExcluded("javaxplussuffix.management.MBeanException"));
assertTrue(manager.isExcluded("java.lang.Exception"));
assertFalse(manager.isExcluded("javaplussuffix.lang.Exception"));
assertFalse(manager.isExcluded("com.example.Moof"));
}
/*
* This test intends to simulate what happens when differing class loaders hold references to the
* same named class - something that would happen in the context of an app server. We need to
* ensure that the AutoSerializableManager tracks these as separate classes even though they might
* have the same name.
*/
@Test
public void testMultipleClassLoaders() throws Exception {
setupSerializer(stdSerializableClasses);
ChildFirstClassLoader cfcl =
new ChildFirstClassLoader(javaClassPathToUrl(), this.getClass().getClassLoader());
cfcl.addIncludedClass("org\\.apache.*");
// Need to exclude DomainObject as that is what the newly created objects
// get cast to.
cfcl.addExcludedClass(".*DomainObject");
Class clazz = cfcl.loadClass("org.apache.geode.pdx.DomainObjectClassLoadable");
// Create our object with a special class loader
DomainObject obj1 = (DomainObject) clazz.newInstance();
obj1.set("string_0", "test string value");
obj1.set("long_0", 99L);
HeapDataOutputStream out1 = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(obj1, out1);
PdxInstance pdxIn = DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out1.toByteArray())));
assertEquals(99L, pdxIn.getField("long_0"));
assertEquals("test string value", pdxIn.getField("string_0"));
// Create our object with the 'standard' class loader
DomainObject obj2 = new DomainObjectClassLoadable();
obj2.set("string_0", "different string value");
obj2.set("long_0", 1009L);
// They are definitely not the same class
assertFalse(obj1.getClass() == obj2.getClass());
HeapDataOutputStream out2 = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(obj2, out2);
pdxIn = DataSerializer
.readObject(new DataInputStream(new ByteArrayInputStream(out2.toByteArray())));
assertEquals(1009L, pdxIn.getField("long_0"));
assertEquals("different string value", pdxIn.getField("string_0"));
// Make sure the manager is holding separate classes
assertEquals(2, manager.getClassMap().size());
}
private URL[] javaClassPathToUrl() throws MalformedURLException {
List<URL> urls = new ArrayList<URL>();
String classPathStr = System.getProperty("java.class.path");
if (classPathStr != null) {
String[] cpList = classPathStr.split(System.getProperty("path.separator"));
for (String u : cpList) {
urls.add(new File(u).toURI().toURL());
}
}
return urls.toArray(new URL[] {});
}
/*
* Custom class loader which will attempt to load classes before delegating to the parent.
*/
private class ChildFirstClassLoader extends URLClassLoader {
private List<String> includedClasses = new ArrayList<String>();
private List<String> excludedClasses = new ArrayList<String>();
public ChildFirstClassLoader() {
super(new URL[] {});
}
public ChildFirstClassLoader(URL[] urls) {
super(urls);
}
public ChildFirstClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addIncludedClass(String clazz) {
includedClasses.add(clazz);
}
public void addExcludedClass(String clazz) {
excludedClasses.add(clazz);
}
public boolean isRelevant(String clazz) {
boolean result = false;
for (String s : includedClasses) {
if (clazz.matches(s)) {
result = true;
break;
}
}
for (String s : excludedClasses) {
if (clazz.matches(s)) {
result = false;
break;
}
}
return result;
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
@Override
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* We override the parent-first behavior established by java.lang.Classloader.
*/
@Override
public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = null;
if (isRelevant(name)) {
// First, check if the class has already been loaded
c = findLoadedClass(name);
// if not loaded, search the local (child) resources
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException cnfe) {
// ignore
}
}
}
// if we could not find it, delegate to parent
// Note that we don't attempt to catch any ClassNotFoundException
if (c == null) {
if (getParent() != null) {
c = getParent().loadClass(name);
} else {
c = getSystemClassLoader().loadClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}