jsnapshot
is a super simple set of utilities to take and handle snapshots of your POJOs. It requires Java 8+.
jsnapshot
is distribuited through jcenter
.
In your pom.xml
:
...
<repositories>
...
<repository>
<id>jcenter</id>
<url>https://jcenter.bintray.com/</url>
</repository>
...
</repositories>
<dependencies>
...
<dependency>
<groupId>com.labbati</groupId>
<artifactId>jsnapshot</artifactId>
<version>0.2.14</version>
<type>pom</type>
</dependency>
...
</dependencies>
In your build.gradle
:
repositories {
...
jcenter()
}
dependencies {
...
compile 'com.labbati:jsnapshot:0.2.14'
}
A snapshot is just a bunch of values wrapped in an instance of Snapshot
. The simplest way to start working with jsnapshot
is to add the interface SnapshotCapable
to a POJO.
public class Person implements SnapshotCapable {
private int id;
private String name;
// ... getters and setter ...
@Override
public Snapshot takeSnapshot() {
return new Snapshot(
this::getId,
new SnapshotValue("name or something else", 1),
new SnapshotValue("some other label", nullSafe(() -> getNested().getOther().getName(), "defaultValue"))
);
}
}
Then you can just do Snapshot snapshot = person.takeSnapshot()
. You basically decide which fields get included into the snapshot and with which value. Nothing's magic here, just old plain reliable getters.
As you may have noticed, for some other label
we used SnapshotCapable
's method nullSafe()
. This method is useful when you need to access nested objects to extract the value. If one object down the path is null
, you would get a NullPointerException
. This method makes sure that such exception is trapped.
The first argument to the method is java 8 lambda that just returns the value. The optional second argument is the fallback value that should be returned if NullPointerException
is thrown. If not provided, than the fallback value is null
.
If you take snapshots of a POJO in two different instant of time, let's say
Snapshot before = person.takeSnapshot();
person.setName("Homer");
Snapshot after = person.takeSnapshot();
At this point you can calculate differences between the two snapshots.
DiffCalculator diffCalculator = new SimpleDiffCalculator();
// You can either exclude values that have not changed...
List<DataDiff> diffsWithSkipUnchanged = diffCalculator.calculate(before, after);
// ...or include them
List<DataDiff> diffsWithIncludeUnchanged = diffCalculator.calculate(before, after, true);
The returned value is a list of DataDiff
. DataDiff
is just a value object including a label
, the oldValue
and the newValue
.
By default, the value that you return in the takeSnapshot()
method will be passed as is to the DataDiff
object. With the exception of Date
objects, which are converted to the correspondent value in milliseconds. However, when you build an instance of SimpleDiffCalculator
you can provide your own implementation of ValueConverter
if you do not like the idea.
A final note: obviously, it is not necessary to generate snapshots starting from a SnapshotCapable
. Specifically, if generating the Snapshot
requires complex calculations or the collaboration of other services, feel free to externalize the creation to separate service.
Have fun!
Copyright (c) 2018-present, Luca Abbati