Download SER00-J. Enable serialization compatibility during class evolution

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
SER00-J. Enable serialization compatibility during class
evolution
Once an object of a particular class has been serialized, future refactoring of the class's code often becomes problematic. Specifically, existing
serialized forms (encoded representations) become part of the object's published API and must be supported for an indefinite period. This can be
troublesome from a security perspective; not only does it promote dead code, it also forces the provider to maintain a compatible codebase for the
lifetime of their products.
Classes that implement Serializable without overriding its functionality are said to be using the default serialized form. In the event the class
changes, byte streams produced by users of old versions of the class become incompatible with the new implementation. Consequently,
serializable classes that rely on the default serialized form cannot be evolved without compromising compatibility.
To enable compatible evolution of a serializable class, developers must use a custom serialized form, which is more flexible than a default form.
Specifically,
Use of a custom form severs the dependence of the stream format on the code of the implementing class.
The code generated for deserializing a custom form can handle compatible deviations from the serialized form, like extra fields.
As a result, developers need neither maintain the earlier version of the code nor explicitly support the original serialized form.
Note that compliance with this rule, while necessary, is not sufficient to guarantee compatible evolution of serializable classes. For a full
discussion of compatible evolution of serializable classes, see the Java Object Serialization Specification (version 6), Chapter 5, "Versioning of
Serializable Objects" [Sun 2006].
Noncompliant Code Example
This noncompliant code example implements a GameWeapon class with a serializable field called numOfWeapons and uses the default serialized
form. Any changes to the internal representation of the class can break the existing serialized form.
class GameWeapon implements Serializable {
int numOfWeapons = 10;
public String toString() {
return String.valueOf(numOfWeapons);
}
}
Because this class does not provide a serialVersionUID, the Java Virtual Machine (JVM) assigns it one using implementation-defined
methods. If the class definition changes, the serialVersionUID is also likely to change. Consequently, the JVM will refuse to associate the
serialized form of an object with the class definition when the version IDs are different.
Compliant Solution (serialVersionUID)
In this solution, the class has an explicit serialVersionUID that contains a number unique to this version of the class. The JVM will make a
good-faith effort to deserialize any serialized object with the same class name and version ID.
class GameWeapon implements Serializable {
private static final long serialVersionUID = 24L;
int numOfWeapons = 10;
public String toString() {
return String.valueOf(numOfWeapons);
}
}
Compliant Solution (serialPersistentFields)
Ideally, Serializable should be implemented only for stable classes. One way to maintain the original serialized form and allow the class to
evolve is to use custom serialization with the help of serialPersistentFields. The static and transient qualifiers specify which fields
should not be serialized, whereas the serialPersistentFields field specifies which fields should be serialized. It also relieves the class from
defining the serializable field within the class implementation, decoupling the current implementation from the overall logic. New fields can easily
be added without breaking compatibility across releases.
class WeaponStore implements Serializable {
int numOfWeapons = 10; // Total number of weapons
}
public class GameWeapon implements Serializable {
WeaponStore ws = new WeaponStore();
private static final ObjectStreamField[] serialPersistentFields
= {new ObjectStreamField("ws", WeaponStore.class)};
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
this.ws = (WeaponStore) gf.get("ws", ws);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
ObjectOutputStream.PutField pf = oos.putFields();
pf.put("ws", ws);
oos.writeFields();
}
public String toString() {
return String.valueOf(ws);
}
}
Risk Assessment
Failure to provide a consistent serialization mechanism across releases can limit the extensibility of classes. If classes are extended, compatibility
issues may result.
Rule
Severity
Likelihood
Remediation Cost
Priority
Level
SER00-J
Low
Probable
High
P2
L3
Automated Detection
Automated detection of classes that use the default serialized form is straightforward.
Tool
Version
Checker
Description
SonarQube Java Plugin
3.10
S2057
Implemented
Related Guidelines
MITRE CWE
CWE-589, Call to Non-ubiquitous API
Bibliography
[API 2014]
[Bloch 2008]
Item 74, "Implement Serialization Judiciously"
[Harold 2006]
Section 13.7.5, "serialPersistentFields"
[Sun 2006]
Java Object Serialization Specification