Serialization is recursive progress, which means when you're serializing a complex object, firstly you need to serialize all its properties. The same thing happens with deserialization.
Planet object contains fields of type int, double and java.lang.String which are primitives and don't need special (de)serialization. LocalDate or LocalDateTime aren't primitives and they're serialized and then deserialized with SafeObjectInputStream.
Serialization hack
As it said in java.io.Serializable documentation, objects can modify their serialization behaviour and even delegate serialization to another class by defining method writeReplace.
JavaDoc cite:
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.
Both LocalDate and LocalDateTime utilizes this possibility and define writeReplace method.
As an example, java.time.LocalDate's implementation:
private Object writeReplace() {
return new Ser(Ser.LOCAL_DATE_TYPE, this);
}
java.time.Ser is a package-private final class that is used as a delegate for java.time.* objects.
Hereby, when you're serializing java.time.LocalDate or java.time.LocalDateTime, actually java.time.Ser being serialized.
Custom deserializer
Previously we found out that java.time.LocalDate was serialized as java.time.Ser. Now, let's try to deserialize it with SafeObjectInputStream.
Before deserialization, resolveClass method is called:
@Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!input.getName().equals(Planet.class.getName())) {
throw new InvalidClassException("Unsupported class", input.getName());
}
return super.resolveClass(input);
}
It checks for class name to be equal to Planet.class.getName(), but java.time.Ser is not, that's why you're getting an exception.
Solution
To resolve this issue, you need to add java.time.Ser to the list of trusted classes. I would suggest modifying your SafeObjectInputStream next way:
public class SafeObjectInputStream extends ObjectInputStream {
private final List<String> supportedClasses = List.of(Planet.class.getName(), "java.time.Ser");
public SafeObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!supportedClasses.contains(input.getName())) {
throw new InvalidClassException("Unsupported class ", input.getName());
}
return super.resolveClass(input);
}
}
NOTE: List.of was introduced in Java 9. If your Java version is less than 9, you can replace it with Arrays.asList.