/* * RHQ Management Platform * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.enterprise.server.safeinvoker; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import org.testng.annotations.Test; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility.SerializationType; @Test public class HibernateDetachUtilityTest { /** * This tests the rare, but very possible, condition where two objects * have the same identity hashcode (System.identityHashCode(A) == System.identityHashCode(B)) * but are not identical objects (A != B). We have seen this as a valid condition * on both SUN and IBM JRE implementations. */ public void testIdenticalHashCodesNonIdenticalObjects() throws Exception { class ArrayObject implements Serializable { private static final long serialVersionUID = 1L; long id; Integer[] array; ArrayObject object; // needed for self-referencing ArrayObject(long key, ArrayObject obj, Integer... members) { id = key; object = obj; array = members; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("ArrayObject [id=").append(id).append(", object=").append( (object == null) ? "null" : object.id).append(", array=").append(Arrays.toString(array)) .append("]"); return builder.toString(); } } class DuplicateHashCodeGenerator implements HibernateDetachUtility.HashCodeGenerator { public Integer getHashCode(Object value) { if (value instanceof Integer && ((Integer) value).intValue() >= 2) { return 11111; } else if (value instanceof ArrayObject) { return 22222; } else { return System.identityHashCode(value); } } } ArrayObject array1 = new ArrayObject(1, null, 1, 2, 3); ArrayObject array2 = new ArrayObject(2, array1, 2, 3); array1.object = array2; // now 1 references 2 and 2 reference 1, circular dependency ArrayObject array3 = new ArrayObject(3, array2, 3); array3.object = array3; ArrayList<ArrayObject> array = new ArrayList<ArrayObject>(3); array.add(array1); array.add(array2); array.add(array3); // SANITY CHECK - make sure our setup is as we expect it to be assert array.get(0).array[0] == Integer.valueOf(1); assert array.get(0).array[1] == Integer.valueOf(2); assert array.get(0).array[2] == Integer.valueOf(3); assert array.get(1).array[0] == Integer.valueOf(2); assert array.get(1).array[1] == Integer.valueOf(3); assert array.get(2).array[0] == Integer.valueOf(3); // make sure array1 still references array2 and vice versa assertObjectEquals(array.get(0).object, array.get(1)); assertObjectEquals(array.get(1).object, array.get(0)); // make sure array3 still self-references assertObjectEquals(array.get(2).object, array.get(2)); // simulate rare condition HibernateDetachUtility.hashCodeGenerator = new DuplicateHashCodeGenerator(); HibernateDetachUtility.nullOutUninitializedFields(array, SerializationType.SERIALIZATION); assert array.get(0).array[0] == Integer.valueOf(1); assert array.get(0).array[1] == Integer.valueOf(2); assert array.get(0).array[2] == Integer.valueOf(3); assert array.get(1).array[0] == Integer.valueOf(2); assert array.get(1).array[1] == Integer.valueOf(3); assert array.get(2).array[0] == Integer.valueOf(3); // make sure array1 still references array2 and vice versa assertObjectEquals(array.get(0).object, array.get(1)); assertObjectEquals(array.get(1).object, array.get(0)); // make sure array3 still self-references assertObjectEquals(array.get(2).object, array.get(2)); // put it back the way it was HibernateDetachUtility.hashCodeGenerator = new HibernateDetachUtility.SystemHashCodeGenerator(); HibernateDetachUtility.nullOutUninitializedFields(array, SerializationType.SERIALIZATION); assert array.get(0).array[0] == Integer.valueOf(1); assert array.get(0).array[1] == Integer.valueOf(2); assert array.get(0).array[2] == Integer.valueOf(3); assert array.get(1).array[0] == Integer.valueOf(2); assert array.get(1).array[1] == Integer.valueOf(3); assert array.get(2).array[0] == Integer.valueOf(3); // make sure array1 still references array2 and vice versa assertObjectEquals(array.get(0).object, array.get(1)); assertObjectEquals(array.get(1).object, array.get(0)); // make sure array3 still self-references assertObjectEquals(array.get(2).object, array.get(2)); } private void assertObjectEquals(Object o1, Object o2) { assert o1 == o2 : "Object [" + o1 + "] does not equal object [" + o2 + "]"; } }