All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.pdx.AutoSerializableJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.pdx;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import junit.framework.TestCase;

import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.SerializationException;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.internal.HeapDataOutputStream;
import com.gemstone.gemfire.internal.PdxSerializerObject;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.shared.Version;
import com.gemstone.gemfire.pdx.internal.AutoSerializableManager;
import com.gemstone.gemfire.pdx.internal.PdxInstanceImpl;

import java.io.File;
import java.io.NotSerializableException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class AutoSerializableJUnitTest extends TestCase {

  private GemFireCacheImpl c;

  private AutoSerializableManager manager;

  private ReflectionBasedAutoSerializer serializer;

  private String[] stdSerializableClasses = new String[] {"com.gemstone.gemfire.pdx.DomainObject.*"};

  public AutoSerializableJUnitTest() {
    super();
  }

  @Override
  public void setUp() throws Exception {
    System.setProperty("gemfire.auto.serialization.no.hardcoded.excludes", "true");
  }

  @Override
  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.getInstance(s);
    this.c = (GemFireCacheImpl) new CacheFactory().
    set("mcast-port", "0").
    setPdxReadSerialized(readSerialized).
    setPdxSerializer(s).
    create();
  }
  /*
   * Test basic functionality.
   */
  public void testSanity() throws Exception {
    setupSerializer("com.gemstone.gemfire.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 list = new ArrayList(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("com.gemstone.gemfire.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);
    }
  }

  public void testConcurrentHashMap() throws Exception {
    setupSerializer("java.util.concurrent..*");
    ConcurrentHashMap m = new ConcurrentHashMap();
    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);
  }
  
  public void testMonth() throws Exception {
    setupSerializer(false, false, "com.gemstone.gemfire.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;
    }
    public void writeExternal(ObjectOutput out) throws IOException {
      out.writeInt(v);
    }

    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;
    }
  }
  
  public void testExternalizable() throws Exception {
    setupSerializer("com.gemstone.gemfire.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.
   */
  public void testWriteReplace()  throws Exception {
    setupSerializer("com.gemstone.gemfire.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() {
    }

    public int compare(Object o1, Object o2) {
      return 0;
    }
  }
  /**
   * A serializable with a writeReplace method should use standard serialization.
   */
  public void testComparator()  throws Exception {
    setupSerializer("com.gemstone.gemfire.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 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;
    }
  }

  public void testCheckPortablity() throws Exception {
    setupSerializer(true, "com.gemstone.gemfire.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);
      fail("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);
    }
  }
  
  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");
    }
  }

  public void testIsIdentityField() throws IOException, ClassNotFoundException {
    setupSerializer(new ExplicitIdentityAutoSerializer(false, "com.gemstone.gemfire.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");
    }
  }

  public void testIsFieldIncluded() throws IOException, ClassNotFoundException {
    setupSerializer(new ExplicitIncludedAutoSerializer(false, "com.gemstone.gemfire.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);
      }
    }
  }

  public void testGetFieldName() throws IOException, ClassNotFoundException {
    setupSerializer(new ExplicitFieldNameAutoSerializer(false, "com.gemstone.gemfire.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 void testExtensibility() throws Exception {
    setupSerializer(new BigIntegerAutoSerializer(true, "com.gemstone.gemfire.pdx.AutoSerializableJUnitTest.BigHolder"), false);

    doExtensible("with autoSerializer handling Big*");
  }
  public void testNoExtensibility() throws Exception {
    setupSerializer(false, false, "com.gemstone.gemfire.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);
      }
    }
  }
  
  public void testCHM() throws Exception {
    setupSerializer(new ConcurrentHashMapAutoSerializer(false, "com.gemstone.gemfire.pdx.AutoSerializableJUnitTest.*Holder"), false);
    doCHM("with autoSerializer handling ConcurrentHashMap");
  }
  public void testNoCHM() throws Exception {
    setupSerializer(false, false, "com.gemstone.gemfire.pdx.AutoSerializableJUnitTest.*Holder");
    doCHM("without autoSerializer handling ConcurrentHashMap");
  }
  private void doCHM(String msg) throws IOException, ClassNotFoundException {
    ConcurrentHashMap chm = new ConcurrentHashMap();
    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.
   */
  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);
    assertEquals(0.0d, result.aDouble);
    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.
   */
  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
   */
  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) {
      fail("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.
   */
  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) {
      fail("Expected SerializationException here but got " + ex);
    }
  }

  /*
   * Test basic declarative configuration
   */
  public void testBasicConfig() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObject");
    serializer.init(props);

    assertEquals(4, manager.getFields(DomainObject.class).size());
  }

  /*
   * Test declarative configuration with excludes
   */
  public void testConfigWithExclude1() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObject#exclude=long.*");
    serializer.init(props);

    assertEquals(3, manager.getFields(DomainObject.class).size());
  }

  public void testConfigWithExclude2() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObject#exclude=string.* ,");
    serializer.init(props);

    assertEquals(1, manager.getFields(DomainObject.class).size());
  }

  /*
   * Test use of the identity param
   */
  public void testConfigWithIdentity1() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#identity=long.*");
    serializer.init(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
   */
  public void testConfigWithIdentityAndExclude1() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*");
    serializer.init(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
   */
  public void testConfigWithIdentityAndExclude2() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*#, com.another.class.Foo");
    serializer.init(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
   */
  public void testConfigWithIdentityAndExclude3() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#identity=long.*#exclude=string.*, com.another.class.Foo");
    serializer.init(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
   */
  public void testConfigWithIdentityAndExclude4() throws Exception {
    setupSerializer();
    Properties props = new Properties();
    props.put("classes", "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#exclude=string.*, "
        + "com.gemstone.gemfire.pdx.DomainObjectPdxAuto#exclude=long.*");
    serializer.init(props);

    assertEquals(26, manager.getFields(DomainObjectPdxAuto.class).size());
  }

  /*
   * Test correct config comes back via getConfig
   */
  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.init(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.init(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"));
  }

  /*
   * 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.
   */
  public void testMultipleClassLoaders() throws Exception {
    setupSerializer(stdSerializableClasses);
    ChildFirstClassLoader cfcl = 
        new ChildFirstClassLoader(javaClassPathToUrl(), this.getClass().getClassLoader());
    cfcl.addIncludedClass("com\\.gemstone.*");
    // Need to exclude DomainObject as that is what the newly created objects
    // get cast to.
    cfcl.addExcludedClass(".*DomainObject");
    Class clazz = cfcl.loadClass("com.gemstone.gemfire.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 urls = new ArrayList();
    String classPathStr = System.getProperty("java.class.path");
    if (classPathStr != null) {
      String[] cpList = classPathStr.split(":");
      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 includedClasses = new ArrayList();

    private List excludedClasses = new ArrayList();

    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;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy