Implementing serialization methods is tedious and error prone. To help in the maintenance of handcoded serialization methods Externalizer4J offers static code analysis to address this source of errors. The bytecode of hand written serialization is analyzed for common errors. The type of errors which can be detected will be illustrated on this page.

Note that static code analysis is enabled by default. There is no need to add a special entry in the externalizer4j.properties configuration file!

JDK and other serialization API’s

Although originally designed to use the JDK serialization API Externalizer4J now supports other API’s like Hazelcast’s DataSerializable API for instance.

The different errors will be illustrated using the JDK’s API. But it is important to note that Externalizer4J offers the same code analysis functionality for each of the API’s it supports. This means that the errors described on this page can be detected for each API.

Static code analysis

Externalizer4J use static code analysis of the bytecode, no the source code, to look for 2 types of errors:

  1. field usage
  2. method/API errors

Field usage

Constants

Description: through serialization one can repeatedly create objects which represent the same value. Normally this is not a problem except when the value has a special meaning. If the value is meant to be used as a constant one would expect the same value to be represented by the same object after each deserialization. The same is true when one uses a singleton pattern (see Resource section). To avoid creating special object several times one should use the special readResolve() method as foreseen by the JDK’s serialization algorithm.

Example of the error:

package demo9;

public class ConstantsSubject implement Serializable {
    public static final ConstantsSubject CONSTANTS_SUBJEC_1 = new ConstantsSubject();
    private static final ConstantsSubject CONSTANTS_SUBJEC_2 = new ConstantsSubject();
}

Example of the error report for the example above:

To ensure that objects representing constant always are represented by one and the same object instead of create a new Object every time. The readResolve() method provides a way to implement this constraint.

Wrong field order errors

Description: the order in which fields are serialized and deserialized must be exactly the same. Externalizer4J checks the serialization and deserialization methods to confirm the correct order is used. If the fields have the same (or compatible) type this error may be hard find and result in data corruption.

Example of the error:

package demo12;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import static be.biggerbytes.util.Init.string;

public class WrongOrderSubject implements Serializable {
    String a = string(), b = string();

    public void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.writeObject(a);
        objectOutputStream.writeObject(b);
    }

    public void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        // !!! b before a
        b = (String) objectInputStream.readObject();
        a = (String) objectInputStream.readObject();
    }
}

Example of the error report for the example above:

Static code analysis reports when fields are serialized and deserialized in a different order
Static code analysis reports when fields are serialized and deserialized in a different order

Different field errors

Description: this check looks for errors where the fields being serialized are different from those being deserialized. If the fields have the same (or compatible) type this error may be hard find and result in data corruption. Such errors a often due to refactoring or simply distraction.

Example of the error:

package demo12;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class WrongFieldsSubject implements Serializable {
    String a, b, c;

    public void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.writeObject(a);
        objectOutputStream.writeObject(b);
    }

    public void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        b = (String) objectInputStream.readObject();
        c = (String) objectInputStream.readObject();
    }
}

Example of the error report for the example above:

check-different-fields

Different number of field errors

Description: this static check identifies situations where a different number of field is read or written. Meaning the a one or more fields are either not being serialized or not being deserialized. This will result in strange corruptions and exceptions at runtime.

Example of the error:

package demo12;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import static be.biggerbytes.util.Init.string;

public class WrongNumberOfFieldsSubject implements Serializable {
    String a = string(), b = string();

    public void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.writeObject(a);
    }

    public void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        a = (String) objectInputStream.readObject();
        b = (String) objectInputStream.readObject();
    }
}

Example of the error report for the example above:

check-different-number-of-fields

Method/API errors

Method mismatch

Description: this analysis checks whether the methods used to  serialize and deserialize a field match. For example: serialization with writeInt() means deserialization should use readInt(). If the field type are compatible with the parameter type of the wrong method this error may go unnoticed but and can result in data corruption at runtime.

Example of the error:

package demo12;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class WrongCallsSubject implements Serializable {
    char a, b;

    public void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.writeChar(a);
        objectOutputStream.writeInt(b);
    }

    public void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        a = objectInputStream.readChar();
        b = objectInputStream.readChar();
    }
}

Example of the error report for the example above:

check-method-mismatch

Too many methods

Description of the static check: this static check identifies classes which mix up different serialization methods. For instance implementing the Serializable methods as well as the Externalizable methods. Although the runtime behaviour can be correct this suggest some confusion or is the result of code refactoring.

Example of the error:

package demo12;

import java.io.*;


public class WrongTooManyMethodsSubject implements Externalizable {
    String a;

    public void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        objectOutputStream.writeObject(a);
    }

    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(a);
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        a = (String) in.readObject();
    }
}

Example of the error report for the example above:

check-too-many-methods

Resources

Close Menu