Persistence extensions, add lastChange and nextChange (#4259)

* add lastChange and nextChange

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
Mark Herwege 2024-06-25 13:46:54 +02:00 committed by GitHub
parent 93b53e7847
commit cb65e41445
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 132 additions and 10 deletions

View File

@ -60,6 +60,7 @@ import org.slf4j.LoggerFactory;
* @author Jan N. Klug - Added interval methods and refactoring
* @author Mark Herwege - Changed return types to State for some interval methods to also return unit
* @author Mark Herwege - Extended for future dates
* @author Mark Herwege - lastChange and nextChange methods
*/
@Component(immediate = true)
@NonNullByDefault
@ -323,9 +324,9 @@ public class PersistenceExtensions {
* Query the last historic update time of a given <code>item</code>. The default persistence service is used.
*
* @param item the item for which the last historic update time is to be returned
* @return point in time of the last historic update to <code>item</code>, or <code>null</code> if there are no
* historic persisted updates or the default persistence service is not available or not a
* {@link QueryablePersistenceService}
* @return point in time of the last historic update to <code>item</code>, <code>null</code> if there are no
* historic persisted updates, the state has changed since the last update or the default persistence
* service is not available or not a {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime lastUpdate(Item item) {
return internalAdjacentUpdate(item, false, null);
@ -336,9 +337,9 @@ public class PersistenceExtensions {
*
* @param item the item for which the last historic update time is to be returned
* @param serviceId the name of the {@link PersistenceService} to use
* @return point in time of the last historic update to <code>item</code>, or <code>null</code> if there are no
* historic persisted updates or if persistence service given by <code>serviceId</code> does not refer to an
* available {@link QueryablePersistenceService}
* @return point in time of the last historic update to <code>item</code>, <code>null</code> if there are no
* historic persisted updates, the state has changed since the last update or if persistence service given
* by <code>serviceId</code> does not refer to an available {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime lastUpdate(Item item, @Nullable String serviceId) {
return internalAdjacentUpdate(item, false, serviceId);
@ -371,6 +372,66 @@ public class PersistenceExtensions {
private static @Nullable ZonedDateTime internalAdjacentUpdate(Item item, boolean forward,
@Nullable String serviceId) {
return internalAdjacent(item, forward, false, serviceId);
}
/**
* Query the last historic change time of a given <code>item</code>. The default persistence service is used.
*
* @param item the item for which the last historic change time is to be returned
* @return point in time of the last historic change to <code>item</code>, <code>null</code> if there are no
* historic persisted changes, the state has changed since the last update or the default persistence
* service is not available or not a {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime lastChange(Item item) {
return internalAdjacentChange(item, false, null);
}
/**
* Query for the last historic change time of a given <code>item</code>.
*
* @param item the item for which the last historic change time is to be returned
* @param serviceId the name of the {@link PersistenceService} to use
* @return point in time of the last historic change to <code>item</code> <code>null</code> if there are no
* historic persisted changes, the state has changed since the last update or if persistence service given
* by <code>serviceId</code> does not refer to an available {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime lastChange(Item item, @Nullable String serviceId) {
return internalAdjacentChange(item, false, serviceId);
}
/**
* Query the first future change time of a given <code>item</code>. The default persistence service is used.
*
* @param item the item for which the first future change time is to be returned
* @return point in time of the first future change to <code>item</code>, or <code>null</code> if there are no
* future persisted changes or the default persistence service is not available or not a
* {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime nextChange(Item item) {
return internalAdjacentChange(item, true, null);
}
/**
* Query for the first future change time of a given <code>item</code>.
*
* @param item the item for which the first future change time is to be returned
* @param serviceId the name of the {@link PersistenceService} to use
* @return point in time of the first future change to <code>item</code>, or <code>null</code> if there are no
* future persisted changes or if persistence service given by <code>serviceId</code> does not refer to an
* available {@link QueryablePersistenceService}
*/
public static @Nullable ZonedDateTime nextChange(Item item, @Nullable String serviceId) {
return internalAdjacentChange(item, true, serviceId);
}
private static @Nullable ZonedDateTime internalAdjacentChange(Item item, boolean forward,
@Nullable String serviceId) {
return internalAdjacent(item, forward, true, serviceId);
}
private static @Nullable ZonedDateTime internalAdjacent(Item item, boolean forward, boolean skipEqual,
@Nullable String serviceId) {
String effectiveServiceId = serviceId == null ? getDefaultServiceId() : serviceId;
if (effectiveServiceId == null) {
return null;
@ -385,10 +446,49 @@ public class PersistenceExtensions {
filter.setEndDate(ZonedDateTime.now());
}
filter.setOrdering(forward ? Ordering.ASCENDING : Ordering.DESCENDING);
filter.setPageSize(1);
Iterable<HistoricItem> result = qService.query(filter);
if (result.iterator().hasNext()) {
return result.iterator().next().getTimestamp();
filter.setPageSize(skipEqual ? 1000 : 1);
int startPage = 0;
filter.setPageNumber(startPage);
Iterable<HistoricItem> items = qService.query(filter);
Iterator<HistoricItem> itemIterator = items.iterator();
State state = item.getState();
if (itemIterator.hasNext()) {
if (!skipEqual) {
HistoricItem historicItem = itemIterator.next();
if (!forward && !historicItem.getState().equals(state)) {
// Last persisted state value different from current state value, so it must have updated since
// last persist. We do not know when.
return null;
}
return historicItem.getTimestamp();
} else {
HistoricItem historicItem = itemIterator.next();
int itemCount = 1;
if (!historicItem.getState().equals(state)) {
// Persisted state value different from current state value, so it must have changed, but we do
// not know when when looking backward.
return forward ? historicItem.getTimestamp() : null;
}
while (items != null) {
while (historicItem.getState().equals(state) && itemIterator.hasNext()) {
HistoricItem nextHistoricItem = itemIterator.next();
itemCount++;
if (!nextHistoricItem.getState().equals(state)) {
return forward ? nextHistoricItem.getTimestamp() : historicItem.getTimestamp();
}
historicItem = nextHistoricItem;
}
if (itemCount == filter.getPageSize()) {
filter.setPageNumber(++startPage);
items = qService.query(filter);
itemCount = 0;
} else {
items = null;
}
}
}
}
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)

View File

@ -1532,6 +1532,28 @@ public class PersistenceExtensionsTest {
assertNull(nextUpdate);
}
@Test
public void testLastChange() {
ZonedDateTime lastChange = PersistenceExtensions.lastChange(numberItem, SERVICE_ID);
assertNotNull(lastChange);
assertEquals(ZonedDateTime.of(HISTORIC_END, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), lastChange);
// default persistence service
lastChange = PersistenceExtensions.lastChange(numberItem);
assertNull(lastChange);
}
@Test
public void testNextChange() {
ZonedDateTime nextChange = PersistenceExtensions.nextChange(numberItem, SERVICE_ID);
assertNotNull(nextChange);
assertEquals(ZonedDateTime.of(FUTURE_START, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()), nextChange);
// default persistence service
nextChange = PersistenceExtensions.nextChange(numberItem);
assertNull(nextChange);
}
@Test
public void testDeltaSince() {
State delta = PersistenceExtensions.deltaSince(numberItem,