| /* |
| * 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.DISTRIBUTED_SYSTEM_ID; |
| 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.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.DataInputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.HashMap; |
| |
| 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.cache.CacheClosedException; |
| import org.apache.geode.cache.CacheFactory; |
| import org.apache.geode.internal.HeapDataOutputStream; |
| import org.apache.geode.internal.Version; |
| import org.apache.geode.internal.cache.GemFireCacheImpl; |
| import org.apache.geode.pdx.internal.EnumInfo.PdxInstanceEnumInfo; |
| import org.apache.geode.pdx.internal.PdxInstanceFactoryImpl; |
| import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase; |
| import org.apache.geode.test.junit.categories.SerializationTest; |
| |
| @Category({SerializationTest.class}) |
| public class PdxInstanceJUnitTest extends JUnit4CacheTestCase { |
| |
| private GemFireCacheImpl c; |
| private int allFieldCount; |
| |
| @Before |
| public void setUp() { |
| // make it a loner |
| this.c = (GemFireCacheImpl) new CacheFactory().set(MCAST_PORT, "0") |
| .set(DISTRIBUTED_SYSTEM_ID, "255").setPdxReadSerialized(true).create(); |
| } |
| |
| @After |
| public void tearDown() { |
| this.c.close(); |
| } |
| |
| @Test |
| public void testGetField() throws IOException, ClassNotFoundException { |
| PdxInstance instance = getPdx(new TestPdx() { |
| @Override |
| public void toData(PdxWriter out) { |
| out.writeBoolean("field1", false); |
| out.writeInt("field2", 53); |
| out.writeObject("field3", new TestPdx() { |
| @Override |
| public void toData(PdxWriter writer) { |
| writer.writeString("afield", "hello"); |
| } |
| |
| }); |
| } |
| }); |
| |
| assertEquals(Arrays.asList(new String[] {"field1", "field2", "field3"}), |
| instance.getFieldNames()); |
| assertEquals(instance.getField("field2"), Integer.valueOf(53)); |
| assertEquals(instance.getField("field1"), Boolean.FALSE); |
| PdxInstance fieldInstance = (PdxInstance) instance.getField("field3"); |
| assertEquals(Arrays.asList(new String[] {"afield"}), fieldInstance.getFieldNames()); |
| assertEquals("hello", fieldInstance.getField("afield")); |
| } |
| |
| @Test |
| public void testHashCodeAndEqualsSameType() throws IOException, ClassNotFoundException { |
| PdxInstance instance = getAllFields(0); |
| assertEquals(instance, getAllFields(0)); |
| assertEquals(instance.hashCode(), getAllFields(0).hashCode()); |
| |
| for (int i = 1; i < allFieldCount + 1; i++) { |
| PdxInstance other = getAllFields(i); |
| assertFalse("One field " + i + " hashcode have been unequal but were equal" |
| + instance.getField("field" + (i - 1)) + ", " + other.getField("field" + (i - 1)) + ", " |
| + instance + ", " + other, instance.equals(other)); |
| // Technically, this could be true. If this asserts fails I guess we can just change the test. |
| assertFalse( |
| "One field " + i + " hashcode have been unequal but were equal" + instance + ", " + other, |
| instance.hashCode() == other.hashCode()); |
| } |
| } |
| |
| @Test |
| public void testEquals() throws IOException, ClassNotFoundException { |
| PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testEquals", false, this.c); |
| c.writeInt("intField", 37); |
| PdxInstance pi = c.create(); |
| assertEquals(false, pi.equals(null)); |
| assertEquals(false, pi.equals(new Date(37))); |
| c = PdxInstanceFactoryImpl.newCreator("testEquals", false, this.c); |
| c.writeInt("intField", 37); |
| c.writeInt("intField2", 38); |
| PdxInstance pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date()); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", null); |
| pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {1}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new byte[] {(byte) 1}); |
| pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {1}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {2}); |
| pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {1}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {1}); |
| pi2 = c.create(); |
| assertEquals(pi.hashCode(), pi2.hashCode()); |
| assertEquals(true, pi.equals(pi2)); |
| assertEquals(true, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new int[] {1}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date()); |
| pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date[] {new Date(1)}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date[] {new Date(2)}); |
| pi2 = c.create(); |
| pi.hashCode(); |
| pi2.hashCode(); |
| assertEquals(false, pi.equals(pi2)); |
| assertEquals(false, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date[] {new Date(1)}); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", new Date[] {new Date(1)}); |
| pi2 = c.create(); |
| assertEquals(pi.hashCode(), pi2.hashCode()); |
| assertEquals(true, pi.equals(pi2)); |
| assertEquals(true, pi2.equals(pi)); |
| |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", MyEnum.ONE); |
| pi = c.create(); |
| c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false, this.c); |
| c.writeObject("objField", MyEnum.ONE); |
| pi2 = c.create(); |
| assertEquals(pi.hashCode(), pi2.hashCode()); |
| assertEquals(true, pi.equals(pi2)); |
| assertEquals(true, pi2.equals(pi)); |
| |
| } |
| |
| public enum MyEnum { |
| ONE, TWO |
| }; |
| |
| public enum MyComplexEnum { |
| ONE {}, |
| TWO {} |
| }; |
| |
| public void testPdxComplexEnum() { |
| PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testPdxEnum", false, this.c); |
| c.writeObject("enumField", MyComplexEnum.ONE); |
| PdxInstance pi = c.create(); |
| Object f = pi.getField("enumField"); |
| if (f instanceof PdxInstanceEnumInfo) { |
| PdxInstanceEnumInfo e = (PdxInstanceEnumInfo) f; |
| assertEquals("ONE", e.getName()); |
| GemFireCacheImpl theCache = (GemFireCacheImpl) getCache(); |
| if (theCache == null) { |
| throw new CacheClosedException( |
| "PDX registry is unavailable because the Cache has been closed."); |
| } |
| theCache.getPdxRegistry().flushCache(); |
| assertEquals(MyComplexEnum.ONE, e.getObject()); |
| } else { |
| fail("Expected enumField to be a PdxInstanceEnumInfo but it was a " + f.getClass()); |
| } |
| } |
| |
| public void testPdxSimpleEnum() { |
| PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testPdxEnum", false, this.c); |
| c.writeObject("enumField", MyEnum.ONE); |
| PdxInstance pi = c.create(); |
| Object f = pi.getField("enumField"); |
| if (f instanceof PdxInstanceEnumInfo) { |
| PdxInstanceEnumInfo e = (PdxInstanceEnumInfo) f; |
| assertEquals("ONE", e.getName()); |
| GemFireCacheImpl theCache = (GemFireCacheImpl) getCache(); |
| if (theCache == null) { |
| throw new CacheClosedException( |
| "PDX registry is unavailable because the Cache has been closed."); |
| } |
| theCache.getPdxRegistry().flushCache(); |
| assertEquals(MyEnum.ONE, e.getObject()); |
| } else { |
| fail("Expected enumField to be a PdxInstanceEnumInfo but it was a " + f.getClass()); |
| } |
| } |
| |
| @Test |
| public void testEqualsDifferentTypes() throws IOException, ClassNotFoundException { |
| PdxInstance instance1 = getPdx(new TestPdx() { |
| @Override |
| public void toData(PdxWriter writer) { |
| writer.writeBoolean("field1", true); |
| } |
| }); |
| |
| PdxInstance instance2 = getPdx(new TestPdx() { |
| @Override |
| public void toData(PdxWriter writer) { |
| writer.writeBoolean("field1", true); |
| } |
| }); |
| |
| // These are different classes, so they shouldn't match. |
| assertFalse(instance1.equals(instance2)); |
| assertFalse(instance1.isIdentityField("field1")); |
| } |
| |
| @Test |
| public void testHashCodeEqualsOutOfOrderFields() |
| throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, |
| IllegalAccessException, InvocationTargetException { |
| PdxSerializable serializable1 = getSeparateClassLoadedPdx(true); |
| PdxInstance instance1 = getPdx(serializable1); |
| PdxSerializable serializable2 = getSeparateClassLoadedPdx(false); |
| PdxInstance instance2 = getPdx(serializable2); |
| |
| assertEquals(instance1, instance2); |
| assertEquals(instance1.hashCode(), instance2.hashCode()); |
| } |
| |
| @Test |
| public void testIdentityFields() throws IOException, ClassNotFoundException { |
| PdxInstance instance1 = getPdx(getPdxSerializable(new TestPdx() { |
| @Override |
| public void toData(PdxWriter out) { |
| out.writeInt("field2", 53); |
| out.writeBoolean("field1", false); |
| out.markIdentityField("field2"); |
| } |
| })); |
| |
| PdxInstance instance2 = getPdx(getPdxSerializable(new TestPdx() { |
| @Override |
| public void toData(PdxWriter out) { |
| out.writeInt("field2", 53); |
| out.writeBoolean("field1", true); |
| out.markIdentityField("field2"); |
| } |
| })); |
| |
| assertEquals(instance1, instance2); |
| assertEquals(instance1.hashCode(), instance2.hashCode()); |
| assertFalse(instance1.isIdentityField("field1")); |
| assertTrue(instance1.isIdentityField("field2")); |
| } |
| |
| // This is hack to make sure the classnames are the same |
| // for every call to this method, even if the toDatas are different. |
| private PdxSerializable getPdxSerializable(final TestPdx toData) { |
| return new TestPdx() { |
| @Override |
| public void toData(PdxWriter writer) { |
| toData.toData(writer); |
| } |
| }; |
| } |
| |
| private PdxSerializable getSeparateClassLoadedPdx(boolean field1First) |
| throws ClassNotFoundException, NoSuchMethodException, InstantiationException, |
| IllegalAccessException, InvocationTargetException { |
| ClassLoader parent = Thread.currentThread().getContextClassLoader(); |
| ClassLoader loader1 = new NonDelegatingLoader(parent); |
| Class clazz1 = loader1.loadClass(getClass().getPackage().getName() + ".SeparateClassloaderPdx"); |
| Constructor constructor = clazz1.getConstructor(boolean.class); |
| constructor.setAccessible(true); |
| PdxSerializable serializable = |
| (PdxSerializable) constructor.newInstance(Boolean.valueOf(field1First)); |
| return serializable; |
| } |
| |
| public PdxInstance getAllFields(final int change) throws IOException, ClassNotFoundException { |
| PdxInstance instance = getPdx(new TestPdx() { |
| @Override |
| public void toData(PdxWriter out) { |
| int x = 0; |
| Number serializable1 = new BigInteger("1234"); |
| Number serializable2 = new BigInteger("1235"); |
| Collection collection1 = new ArrayList(); |
| collection1.add(serializable1); |
| collection1.add(serializable2); |
| Collection collection2 = new ArrayList(); |
| collection2.add(serializable2); |
| collection2.add(serializable1); |
| SimpleClass testPdx1 = new SimpleClass(5, (byte) 5); |
| SimpleClass testPdx2 = new SimpleClass(6, (byte) 6); |
| HashMap map1 = new HashMap(); |
| HashMap map2 = new HashMap(); |
| map2.put(serializable1, serializable2); |
| |
| |
| out.writeChar("field" + x++, change == x ? 'c' : 'd'); |
| out.writeBoolean("field" + x++, change == x ? true : false); |
| out.writeByte("field" + x++, (byte) (change == x ? 0x5 : 0x6)); |
| out.writeShort("field" + x++, (short) (change == x ? 7 : 8)); |
| out.writeInt("field" + x++, change == x ? 9 : 10); |
| out.writeLong("field" + x++, change == x ? 55555555 : 666666666); |
| out.writeFloat("field" + x++, change == x ? 23.44f : 23.55f); |
| out.writeDouble("field" + x++, change == x ? 28.7777 : 29.3333); |
| out.writeDate("field" + x++, change == x ? new Date(23L) : new Date(25L)); |
| // out.writeEnum("field" + x++, change==x ? Enum<?> e: ); |
| out.writeString("field" + x++, change == x ? "hello" : "world"); |
| out.writeObject("field" + x++, change == x ? serializable1 : serializable2); |
| out.writeObject("field" + x++, change == x ? testPdx1 : testPdx2); |
| out.writeObject("field" + x++, change == x ? map1 : map2); |
| out.writeObject("field" + x++, change == x ? collection1 : collection2); |
| // out.writeRegion("field" + x++, change==x ? region1: region2); |
| out.writeBooleanArray("field" + x++, |
| change == x ? new boolean[] {false, true} : new boolean[] {true, false}); |
| out.writeCharArray("field" + x++, |
| change == x ? new char[] {'a', 'b'} : new char[] {'a', 'c'}); |
| out.writeByteArray("field" + x++, |
| change == x ? new byte[] {0, 1, 2} : new byte[] {0, 1, 3}); |
| out.writeShortArray("field" + x++, |
| change == x ? new short[] {0, 1, 4} : new short[] {0, 1, 5}); |
| out.writeIntArray("field" + x++, change == x ? new int[] {0, 1, 4} : new int[] {0, 1, 5}); |
| out.writeLongArray("field" + x++, |
| change == x ? new long[] {0, 1, 4} : new long[] {0, 1, 5}); |
| out.writeFloatArray("field" + x++, |
| change == x ? new float[] {0, 1, 4} : new float[] {0, 1, 5}); |
| out.writeDoubleArray("field" + x++, |
| change == x ? new double[] {0, 1, 4} : new double[] {0, 1, 5}); |
| out.writeStringArray("field" + x++, |
| change == x ? new String[] {"a", "b"} : new String[] {"a", "c"}); |
| out.writeObjectArray("field" + x++, |
| change == x ? new Object[] {collection1, serializable1} : new Object[] {serializable2}); |
| out.writeArrayOfByteArrays("field" + x++, |
| change == x ? new byte[][] {{1, 2}} : new byte[][] {{2, 2}}); |
| allFieldCount = x; |
| } |
| }); |
| |
| return instance; |
| } |
| |
| private PdxInstance getPdx(PdxSerializable toData) throws IOException, ClassNotFoundException { |
| HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT); |
| DataSerializer.writeObject(toData, out); |
| return DataSerializer |
| .readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray()))); |
| } |
| |
| |
| |
| abstract static class TestPdx implements PdxSerializable { |
| |
| @Override |
| public void fromData(PdxReader in) {} |
| |
| } |
| |
| } |