Author: bayard
Date: Thu May 29 23:23:45 2008
New Revision: 661577
URL: http://svn.apache.org/viewvc?rev=661577&view=rev
Log:
Applying Joerg's final patch from COLLECTIONS-266, including the unit test that
shows the problem and fixes the problem by making the hashcode transient, and
moving the hashcode implementation such that it can be called from the
deserialization as well as the hashcode method
Modified:
commons/proper/collections/trunk/src/java/org/apache/commons/collections/keyvalue/MultiKey.java
commons/proper/collections/trunk/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java
Modified:
commons/proper/collections/trunk/src/java/org/apache/commons/collections/keyvalue/MultiKey.java
URL:
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/java/org/apache/commons/collections/keyvalue/MultiKey.java?rev=661577&r1=661576&r2=661577&view=diff
==============================================================================
---
commons/proper/collections/trunk/src/java/org/apache/commons/collections/keyvalue/MultiKey.java
(original)
+++
commons/proper/collections/trunk/src/java/org/apache/commons/collections/keyvalue/MultiKey.java
Thu May 29 23:23:45 2008
@@ -16,6 +16,8 @@
*/
package org.apache.commons.collections.keyvalue;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Arrays;
@@ -54,7 +56,7 @@
/** The individual keys */
private final Object[] keys;
/** The cached hashCode */
- private final int hashCode;
+ private transient int hashCode;
/**
* Constructor taking two keys.
@@ -164,13 +166,7 @@
this.keys = keys;
}
- int total = 0;
- for (int i = 0; i < keys.length; i++) {
- if (keys[i] != null) {
- total ^= keys[i].hashCode();
- }
- }
- hashCode = total;
+ calculateHashCode(keys);
}
//-----------------------------------------------------------------------
@@ -255,4 +251,29 @@
return "MultiKey" + Arrays.asList(keys).toString();
}
+ /**
+ * Calculate the hash code of the instance using the provided keys.
+ * @param keys
+ */
+ private void calculateHashCode(Object[] keys)
+ {
+ int total = 0;
+ for (int i = 0; i < keys.length; i++) {
+ if (keys[i] != null) {
+ total ^= keys[i].hashCode();
+ }
+ }
+ hashCode = total;
+ }
+
+ /**
+ * Recalculate the hash code after deserialization. The hash code of
some
+ * keys might have change (hash codes based on the system hash code are
+ * only stable for the same process).
+ * @return the instance with recalculated hash code
+ */
+ private Object readResolve() {
+ calculateHashCode(keys);
+ return this;
+ }
}
Modified:
commons/proper/collections/trunk/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java
URL:
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java?rev=661577&r1=661576&r2=661577&view=diff
==============================================================================
---
commons/proper/collections/trunk/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java
(original)
+++
commons/proper/collections/trunk/src/test/org/apache/commons/collections/keyvalue/TestMultiKey.java
Thu May 29 23:23:45 2008
@@ -16,7 +16,15 @@
*/
package org.apache.commons.collections.keyvalue;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import junit.framework.Assert;
import junit.framework.Test;
@@ -206,4 +214,58 @@
Assert.assertTrue(mk1.equals(null) == false);
}
+ static class SystemHashCodeSimulatingKey implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final String name;
+ private int hashCode = 1;
+
+ public SystemHashCodeSimulatingKey(String name)
+ {
+ this.name = name;
+ }
+
+ public boolean equals(Object obj)
+ {
+ return obj instanceof SystemHashCodeSimulatingKey
+ &&
name.equals(((SystemHashCodeSimulatingKey)obj).name);
+ }
+
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ private Object readResolve() {
+ hashCode=2; // simulate different hashCode after
deserialization in another process
+ return this;
+ }
+ }
+
+ public void testEqualsAfterSerialization() throws IOException,
ClassNotFoundException
+ {
+ SystemHashCodeSimulatingKey sysKey = new
SystemHashCodeSimulatingKey("test");
+ MultiKey mk = new MultiKey(ONE, sysKey);
+ Map map = new HashMap();
+ map.put(mk, TWO);
+
+ // serialize
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ out.writeObject(sysKey);
+ out.writeObject(map);
+ out.close();
+
+ // deserialize
+ ByteArrayInputStream bais = new
ByteArrayInputStream(baos.toByteArray());
+ ObjectInputStream in = new ObjectInputStream(bais);
+ sysKey = (SystemHashCodeSimulatingKey)in.readObject(); // simulate
deserialization in another process
+ Map map2 = (Map) in.readObject();
+ in.close();
+
+ assertEquals(2, sysKey.hashCode()); // different hashCode now
+
+ MultiKey mk2 = new MultiKey(ONE, sysKey);
+ assertEquals(TWO, map2.get(mk2));
+ }
}