v3.3.0
- ☔️ Fix NPE in hollow history triggered by removing a primary key across data versions
- ✨ New
ObjectModificationValidatorto validate all objects changed in a cycle - ✨ Ability to specify time zone in History UI
- ✨ Allow callers to block on or receive a callback for a consumer's initial load of data
- ☔️ Make it less likely to inadvertently share refresh listeners across consumers
- ☔️ Explicit failure when attempting to add null to collections or maps (#253)
- ✨ Ensure exceptions are reported, rather than swallowed, with executor tasks so that failures surface closer to the source.
- ✨ More precise exception catching
- ✨ Improve performance when converting a write state to a read state
- ✨ Replace
.printStackTrace()with SEVERE log (#257) - ✨ Reduce boilerplate code using Java 8 idioms & miscellaneous cleanup
ObjectModificationValidator
This validator runs a provided BiPredicate on every instance of an object that was modified in a cycle. This "filter" function is called with the old object as the first argument and the new object as the second argument. If the function returns false, this validator is marked as failed and the producer cycle fails.
Note that the filter function is not called with any objects that were added or removed.
Also note that this validator can only be used on types that have a primary key.
Example usage - note that Movie and MovieAPI are generated classes:
producerBuilder.withValidator(new ObjectModificationValidator<MovieAPI, Movie>(
"Movie",
(oldMovie, newMovie) -> oldMovie.getName().equals(newMovie.getName()),
MovieAPI::new,
(api, ordinal) -> api.getMovie(ordinal)));
Future for a consumer's initial data load
The method getInitialLoad() returns a future which is completed when a consumer has loaded its first data set after construction. This allows callers to block, for instance, until a consumer is ready:
HollowConsumer consumer = builder
...
.build();
CompletableFuture<Long> future = consumer.getInitialLoad();
future.join(); // block indefinitely
Other methods on CompletableFuture allow callers to block with a timeout or supply an async callback.
This should replace the need for an explicit call to triggerRefresh() on consumers constructed with an announcement watcher (note: the AnnouncementWatcher implementation still needs to call triggerRefresh() or triggerAsyncRefresh()).