/*
* 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 + "]";
}
}