fingerprint

fun fingerprint(stateType: Class<*>, mapper: JsonMapper = this.mapper): String

Identifies the shape of stateType's JSON as mapper serializes it: the SHA-256 hash of a structural description built from what Jackson's format visitor reports, that is from the serializers the mapper would use, after naming strategies, ignored properties and the Kotlin module apply (shape):

  • a bean is {name:shape,...} over its serialized properties, sorted by name, each property's declared type described in turn (type arguments resolved);

  • an enum is enum(A,B,...) over its serialized constants, sorted;

  • a primitive, a container or a java.*, javax.* or kotlin.* type is its class name with its type arguments described, java.util.List<{...}>; an array is its element's shape followed by [];

  • a type Jackson writes as a scalar (a custom serializer, @JsonValue) is its class name;

  • a type met again inside itself is a back-reference ^n, n levels up, so recursive states terminate.

The description names no class of the state graph outside the platform packages and sorts every list, so it does not depend on the JVM run, declaration order or map ordering, and moving or renaming a class keeps it: the state's JSON does not carry class names. Adding, removing, renaming or retyping a serialized property, of the state or of any type it holds, changes it, as does adding or removing an enum constant. Nullability, required-ness and subtypes behind an abstract property type are not part of it: a snapshot that does not decode any more is replayed from the log as well.