You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Rico Neubauer (Jira)" <ji...@apache.org> on 2023/08/01 09:09:00 UTC
[jira] [Created] (LANG-1705) commons-lang3:3.13.0 introduces breaking change in SerializationUtils
Rico Neubauer created LANG-1705:
-----------------------------------
Summary: commons-lang3:3.13.0 introduces breaking change in SerializationUtils
Key: LANG-1705
URL: https://issues.apache.org/jira/browse/LANG-1705
Project: Commons Lang
Issue Type: Bug
Components: lang.*
Affects Versions: 3.13.0
Reporter: Rico Neubauer
Attachments: SerializationTest.java
Want to report a change in behavior, which is not necessarily a bug/regression, but might get triggered by crude classes getting serialized.
With this commit: 357951ff5c28dbd724611e8d41e23686f09a164a released in 3.13.0 SerializationUtils#clone changed a bit that it now makes a cast to the expected type.
This sounds reasonable, but might break existing code that serializes classes that have parent-child dependencies and overwrite #writeObject in a certain way.
I'll provide a standalone test case below with comments that should make it clear.
Issue is in case writeObject changes the type of object that gets serialized, then class obtained from in#readObject of the previously serialized object will be different from the expected one.
This most probably is a violation of:
{noformat}
"The object returned should be either of the same type as the object passed in or an object that when read and resolved will result in an object of a type that is compatible with all references to the object."
https://docs.oracle.com/en/java/javase/11/docs/specs/serialization/output.html{noformat}
and also the contract of #clone, so could be treated as "told you", anyhow such code may exist and then #clone will fail with:
{code:java}
java.lang.ClassCastException: Cannot cast com.seeburger.test.SerializationTest$Parent to com.seeburger.test.SerializationTest$Child
at java.base/java.lang.Class.cast(Class.java:3605)
at org.apache.commons.lang3.SerializationUtils.clone(SerializationUtils.java:148)
{code}
Standalone test for reproducing the issue with 3.13.0:
{code:java}
import static org.junit.Assert.assertEquals;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Objects;
import org.apache.commons.lang3.SerializationUtils;
import org.junit.Test;
import com.seeburger.frame.integration.adapter.AdapterException;
public class SerializationTest
{
@Test
public void serializeParent() throws AdapterException, RuntimeException
{
// this test will pass with any version
Parent parent = new Parent(true);
Parent clonedParent = SerializationUtils.clone(parent);
assertEquals(parent, clonedParent);
}
@Test
public void serializeChild() throws AdapterException, RuntimeException
{
Child child = new Child(true);
// this test will pass with org.apache.commons:commons-lang3:3.12.0,
// but will fail with 3.13.0
Parent clonedChild = SerializationUtils.clone(child);
assertEquals(new Parent(true), clonedChild);
}
static class Parent implements Serializable
{
private static final long serialVersionUID = 1L;
protected boolean someField;
Parent(boolean someField)
{
this.someField = someField;
}
protected Parent(Parent parent)
{
this.someField = parent.someField;
}
/**
* protected modifier lets also child's serialization call this
*/
protected Object writeReplace() throws ObjectStreamException
{
return new Parent(this);
}
@Override
public int hashCode()
{
return Objects.hash(someField);
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Parent other = (Parent)obj;
return someField == other.someField;
}
}
static class Child extends Parent
{
private static final long serialVersionUID = 2L;
Child(boolean someField)
{
super(someField);
}
}
}
{code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)