/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.model.persistence.extensions;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import org.eclipse.smarthome.core.items.Item;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.persistence.FilterCriteria;
import org.eclipse.smarthome.core.persistence.FilterCriteria.Ordering;
import org.eclipse.smarthome.core.persistence.HistoricItem;
import org.eclipse.smarthome.core.persistence.PersistenceService;
import org.eclipse.smarthome.core.persistence.PersistenceServiceRegistry;
import org.eclipse.smarthome.core.persistence.QueryablePersistenceService;
import org.eclipse.smarthome.core.types.State;
import org.joda.time.DateTime;
import org.joda.time.base.AbstractInstant;
import org.slf4j.LoggerFactory;
/**
* This class provides static methods that can be used in automation rules
* for using persistence services
*
* @author Kai Kreuzer - Initial contribution and API and refactoring for PersistenceServiceRegistryImpl
* @author Thomas Eichstaedt-Engelen
* @author Chris Jackson
* @author Gaƫl L'hopital
* @author Jan N. Klug
* @author John Cocula
*
*/
public class PersistenceExtensions {
private static PersistenceServiceRegistry registry;
public PersistenceExtensions() {
// default constructor, necessary for osgi-ds
}
protected void setPersistenceServiceRegistry(PersistenceServiceRegistry registry) {
PersistenceExtensions.registry = registry;
}
protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry registry) {
PersistenceExtensions.registry = null;
}
private static PersistenceService getService(String serviceId) {
PersistenceService service = null;
if (registry != null) {
if (serviceId != null) {
service = registry.get(serviceId);
} else {
service = registry.getDefault();
}
}
return service;
}
private static String getDefaultServiceId() {
if (registry != null) {
String id = registry.getDefaultId();
if (id != null) {
return id;
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no default persistence service configured!");
}
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("PersistenceServiceRegistryImpl is not available!");
}
return null;
}
/**
* Persists the state of a given <code>item</code> through a {@link PersistenceService} identified
* by the <code>serviceId</code>.
*
* @param item the item to store
* @param serviceId the name of the {@link PersistenceService} to use
*/
public static void persist(Item item, String serviceId) {
PersistenceService service = getService(serviceId);
if (service != null) {
service.store(item);
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no persistence service registered with the id '{}'", serviceId);
}
}
/**
* Persists the state of a given <code>item</code> through the default persistence service.
*
* @param item the item to store
*/
public static void persist(Item item) {
persist(item, getDefaultServiceId());
}
/**
* Retrieves the historic item for a given <code>item</code> at a certain point in time through the default
* persistence service.
*
* @param item the item for which to retrieve the historic item
* @param timestamp the point in time for which the historic item should be retrieved
* @return the historic item at the given point in time, or <code>null</code> if no historic item could be found,
* the default persistence service is not available or does not refer to a
* {@link QueryablePersistenceService}
*/
public static HistoricItem historicState(Item item, AbstractInstant timestamp) {
return historicState(item, timestamp, getDefaultServiceId());
}
/**
* Retrieves the historic item for a given <code>item</code> at a certain point in time through a
* {@link PersistenceService} identified by the <code>serviceId</code>.
*
* @param item the item for which to retrieve the historic item
* @param timestamp the point in time for which the historic item should be retrieved
* @param serviceId the name of the {@link PersistenceService} to use
* @return the historic item at the given point in time, or <code>null</code> if no historic item could be found or
* if the provided <code>serviceId</code> does not refer to an available
* {@link QueryablePersistenceService}
*/
public static HistoricItem historicState(Item item, AbstractInstant timestamp, String serviceId) {
PersistenceService service = getService(serviceId);
if (service instanceof QueryablePersistenceService) {
QueryablePersistenceService qService = (QueryablePersistenceService) service;
FilterCriteria filter = new FilterCriteria();
filter.setEndDate(timestamp.toDate());
filter.setItemName(item.getName());
filter.setPageSize(1);
filter.setOrdering(Ordering.DESCENDING);
Iterable<HistoricItem> result = qService.query(filter);
if (result.iterator().hasNext()) {
return result.iterator().next();
} else {
return null;
}
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no queryable persistence service registered with the id '{}'", serviceId);
return null;
}
}
/**
* Checks if the state of a given <code>item</code> has changed since a certain point in time.
* The default persistence service is used.
*
* @param item the item to check for state changes
* @param timestamp the point in time to start the check
* @return <code>true</code> if item state had changed, <code>false</code> if it hasn't or if the default
* persistence service does not refer to a {@link QueryablePersistenceService}, or <code>null</code> if the
* default persistence service is not available
*/
public static Boolean changedSince(Item item, AbstractInstant timestamp) {
return changedSince(item, timestamp, getDefaultServiceId());
}
/**
* Checks if the state of a given <code>item</code> has changed since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to check for state changes
* @param timestamp the point in time to start the check
* @param serviceId the name of the {@link PersistenceService} to use
* @return <code>true</code> if item state has changed, or <code>false</code> if it hasn't or if the given
* <code>serviceId</code> does not refer to an available {@link QueryablePersistenceService}
*/
public static Boolean changedSince(Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
Iterator<HistoricItem> it = result.iterator();
HistoricItem itemThen = historicState(item, timestamp);
if (itemThen == null) {
// Can't get the state at the start time
// If we've got results more recent that this, it must have changed
return it.hasNext();
}
State state = itemThen.getState();
while (it.hasNext()) {
HistoricItem hItem = it.next();
if (state != null && !hItem.getState().equals(state)) {
return true;
}
state = hItem.getState();
}
return false;
}
/**
* Checks if the state of a given <code>item</code> has been updated since a certain point in time.
* The default persistence service is used.
*
* @param item the item to check for state updates
* @param timestamp the point in time to start the check
* @return <code>true</code> if item state was updated, <code>false</code> if either item has not been updated since
* <code>timestamp</code> or if the default persistence does not refer to a
* {@link QueryablePersistenceService}, or <code>null</code> if the default persistence service is not
* available
*/
public static Boolean updatedSince(Item item, AbstractInstant timestamp) {
return updatedSince(item, timestamp, getDefaultServiceId());
}
/**
* Checks if the state of a given <code>item</code> has changed since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to check for state changes
* @param timestamp the point in time to start the check
* @param serviceId the name of the {@link PersistenceService} to use
* @return <code>true</code> if item state was updated or <code>false</code> if either the item has not been updated
* since <code>timestamp</code> or if the given <code>serviceId</code> does not refer to a
* {@link QueryablePersistenceService}
*/
public static Boolean updatedSince(Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
if (result.iterator().hasNext()) {
return true;
} else {
return false;
}
}
/**
* Gets the historic item with the maximum value of the state of a given <code>item</code> since
* a certain point in time. The default persistence service is used.
*
* @param item the item to get the maximum state value for
* @param timestamp the point in time to start the check
* @return a historic item with the maximum state value since the given point in time, or <code>null</code> if the
* default persistence service is not available, or a {@link HistoricItem} constructed from the
* <code>item</code> if the default persistence service does not refer to a
* {@link QueryablePersistenceService}
*/
public static HistoricItem maximumSince(Item item, AbstractInstant timestamp) {
return maximumSince(item, timestamp, getDefaultServiceId());
}
/**
* Gets the historic item with the maximum value of the state of a given <code>item</code> since
* a certain point in time. The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the maximum state value for
* @param timestamp the point in time to start the check
* @param serviceId the name of the {@link PersistenceService} to use
* @return a {@link HistoricItem} with the maximum state value since the given point in time, or a
* {@link HistoricItem} constructed from the <code>item</code>'s state if <code>item</code>'s state is the
* maximum value or if the given <code>serviceId</code> does not refer to an available
* {@link QueryablePersistenceService}
*/
public static HistoricItem maximumSince(final Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
Iterator<HistoricItem> it = result.iterator();
HistoricItem maximumHistoricItem = null;
DecimalType maximum = (DecimalType) item.getStateAs(DecimalType.class);
while (it.hasNext()) {
HistoricItem historicItem = it.next();
State state = historicItem.getState();
if (state instanceof DecimalType) {
DecimalType value = (DecimalType) state;
if (maximum == null || value.compareTo(maximum) > 0) {
maximum = value;
maximumHistoricItem = historicItem;
}
}
}
if (maximumHistoricItem == null && maximum != null) {
// the maximum state is the current one, so construct a historic item on the fly
final DecimalType state = maximum;
return new HistoricItem() {
@Override
public Date getTimestamp() {
return Calendar.getInstance().getTime();
}
@Override
public State getState() {
return state;
}
@Override
public String getName() {
return item.getName();
}
};
} else {
return maximumHistoricItem;
}
}
/**
* Gets the historic item with the minimum value of the state of a given <code>item</code> since
* a certain point in time. The default persistence service is used.
*
* @param item the item to get the minimum state value for
* @param timestamp the point in time from which to search for the minimum state value
* @return the historic item with the minimum state value since the given point in time, <code>null</code> if the
* default persistence service is not available, or a {@link HistoricItem} constructed from the
* <code>item</code>'s state if <code>item</code>'s state is the minimum value or if the default persistence
* service does not refer to an available {@link QueryablePersistenceService}
*/
public static HistoricItem minimumSince(Item item, AbstractInstant timestamp) {
return minimumSince(item, timestamp, getDefaultServiceId());
}
/**
* Gets the historic item with the minimum value of the state of a given <code>item</code> since
* a certain point in time. The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the minimum state value for
* @param timestamp the point in time from which to search for the minimum state value
* @param serviceId the name of the {@link PersistenceService} to use
* @return the historic item with the minimum state value since the given point in time, or a {@link HistoricItem}
* constructed from the <code>item</code>'s state if <code>item</code>'s state is the minimum value or if
* the given <code>serviceId</code> does not refer to an available {@link QueryablePersistenceService}
*/
public static HistoricItem minimumSince(final Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
Iterator<HistoricItem> it = result.iterator();
HistoricItem minimumHistoricItem = null;
DecimalType minimum = (DecimalType) item.getStateAs(DecimalType.class);
while (it.hasNext()) {
HistoricItem historicItem = it.next();
State state = historicItem.getState();
if (state instanceof DecimalType) {
DecimalType value = (DecimalType) state;
if (minimum == null || value.compareTo(minimum) < 0) {
minimum = value;
minimumHistoricItem = historicItem;
}
}
}
if (minimumHistoricItem == null && minimum != null) {
// the minimal state is the current one, so construct a historic item on the fly
final DecimalType state = minimum;
return new HistoricItem() {
@Override
public Date getTimestamp() {
return Calendar.getInstance().getTime();
}
@Override
public State getState() {
return state;
}
@Override
public String getName() {
return item.getName();
}
};
} else {
return minimumHistoricItem;
}
}
/**
* Gets the average value of the state of a given <code>item</code> since a certain point in time.
* The default persistence service is used.
*
* @param item the item to get the average state value for
* @param timestamp the point in time from which to search for the average state value
* @return the average state values since <code>timestamp</code>, <code>null</code> if the default persistence
* service is not available, or the state of the given <code>item</code> if no previous states could be
* found or if the default persistence service does not refer to an available
* {@link QueryablePersistenceService}
*/
public static DecimalType averageSince(Item item, AbstractInstant timestamp) {
return averageSince(item, timestamp, getDefaultServiceId());
}
/**
* Gets the average value of the state of a given <code>item</code> since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the average state value for
* @param timestamp the point in time from which to search for the average state value
* @param serviceId the name of the {@link PersistenceService} to use
* @return the average state values since <code>timestamp</code>, or the state of the given <code>item</code> if no
* previous states could be found or if the persistence service given by <code>serviceId</code> does not
* refer to an available {@link QueryablePersistenceService}
*/
public static DecimalType averageSince(Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
Iterator<HistoricItem> it = result.iterator();
BigDecimal total = BigDecimal.ZERO;
BigDecimal avgValue, timeSpan;
DecimalType lastState = null, thisState = null;
BigDecimal lastTimestamp = null, thisTimestamp = null;
BigDecimal firstTimestamp = null;
while (it.hasNext()) {
HistoricItem thisItem = it.next();
State state = thisItem.getState();
if (state instanceof DecimalType) {
thisState = (DecimalType) state;
thisTimestamp = BigDecimal.valueOf(thisItem.getTimestamp().getTime());
if (firstTimestamp == null) {
firstTimestamp = thisTimestamp;
} else {
avgValue = (thisState.toBigDecimal().add(lastState.toBigDecimal())).divide(BigDecimal.valueOf(2),
MathContext.DECIMAL64);
timeSpan = thisTimestamp.subtract(lastTimestamp);
total = total.add(avgValue.multiply(timeSpan, MathContext.DECIMAL64));
}
lastTimestamp = thisTimestamp;
lastState = thisState;
}
}
if (lastState != null) {
thisState = (DecimalType) item.getStateAs(DecimalType.class);
thisTimestamp = BigDecimal.valueOf((new DateTime()).getMillis());
avgValue = (thisState.toBigDecimal().add(lastState.toBigDecimal())).divide(BigDecimal.valueOf(2),
MathContext.DECIMAL64);
timeSpan = thisTimestamp.subtract(lastTimestamp);
total = total.add(avgValue.multiply(timeSpan, MathContext.DECIMAL64));
}
if (thisTimestamp != null) {
timeSpan = thisTimestamp.subtract(firstTimestamp, MathContext.DECIMAL64);
BigDecimal average = total.divide(timeSpan, MathContext.DECIMAL64);
return new DecimalType(average);
} else {
return null;
}
}
/**
* Gets the sum of the state of a given <code>item</code> since a certain point in time.
* The default persistence service is used.
*
* @param item the item for which we will sum its persisted state values since <code>timestamp</code>
* @param timestamp the point in time from which to start the summation
* @return the sum of the state values since <code>timestamp</code>, <code>null</code> if the default persistence
* service is not available, or {@link DecimalType.ZERO} if no historic states could be found or if the
* default persistence service does not refer to a {@link QueryablePersistenceService}
*/
public static DecimalType sumSince(Item item, AbstractInstant timestamp) {
return sumSince(item, timestamp, getDefaultServiceId());
}
/**
* Gets the sum of the state of a given <code>item</code> since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item for which we will sum its persisted state values since <code>timestamp</code>
* @param timestamp the point in time from which to start the summation
* @param serviceId the name of the {@link PersistenceService} to use
* @return the sum of the state values since the given point in time, or {@link DecimalType.ZERO} if no historic
* states could be found for the <code>item</code> or if <code>serviceId</code> does no refer to a
* {@link QueryablePersistenceService}
*/
public static DecimalType sumSince(Item item, AbstractInstant timestamp, String serviceId) {
Iterable<HistoricItem> result = getAllStatesSince(item, timestamp, serviceId);
Iterator<HistoricItem> it = result.iterator();
BigDecimal sum = BigDecimal.ZERO;
while (it.hasNext()) {
State state = it.next().getState();
if (state instanceof DecimalType) {
sum = sum.add(((DecimalType) state).toBigDecimal());
}
}
return new DecimalType(sum);
}
private static Iterable<HistoricItem> getAllStatesSince(Item item, AbstractInstant timestamp, String serviceId) {
PersistenceService service = getService(serviceId);
if (service instanceof QueryablePersistenceService) {
QueryablePersistenceService qService = (QueryablePersistenceService) service;
FilterCriteria filter = new FilterCriteria();
filter.setBeginDate(timestamp.toDate());
filter.setItemName(item.getName());
filter.setOrdering(Ordering.ASCENDING);
return qService.query(filter);
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no queryable persistence service registered with the id '{}'", serviceId);
return Collections.emptySet();
}
}
/**
* Query the last update time of a given <code>item</code>. The default persistence service is used.
*
* @param item the item for which the last update time is to be returned
* @return point in time of the last update to <code>item</code>, or <code>null</code> if there are no previously
* persisted updates or the default persistence service is not available or a
* {@link QueryablePersistenceService}
*/
public static AbstractInstant lastUpdate(Item item) {
return lastUpdate(item, getDefaultServiceId());
}
/**
* Query for the last update time of a given <code>item</code>.
*
* @param item the item for which the last update time is to be returned
* @param serviceId the name of the {@link PersistenceService} to use
* @return last time <code>item</code> was updated, or <code>null</code> if there are no previously
* persisted updates or if persistence service given by <code>serviceId</code> does not refer to an
* available {@link QueryablePersistenceService}
*/
public static AbstractInstant lastUpdate(Item item, String serviceId) {
PersistenceService service = getService(serviceId);
if (service instanceof QueryablePersistenceService) {
QueryablePersistenceService qService = (QueryablePersistenceService) service;
FilterCriteria filter = new FilterCriteria();
filter.setItemName(item.getName());
filter.setOrdering(Ordering.DESCENDING);
filter.setPageSize(1);
Iterable<HistoricItem> result = qService.query(filter);
if (result.iterator().hasNext()) {
return new DateTime(result.iterator().next().getTimestamp());
} else {
return null;
}
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no queryable persistence service registered with the id '{}'", serviceId);
return null;
}
}
/**
* Gets the difference value of the state of a given <code>item</code> since a certain point in time.
* The default persistence service is used.
*
* @param item the item to get the average state value for
* @param timestamp the point in time from which to compute the delta
* @return the difference between now and then, or <code>null</code> if there is no default persistence
* service available, the default persistence service is not a {@link QueryablePersistenceService}, or if
* there is no persisted state for the given <code>item</code> at the given <code>timestamp</code> available
* in the default persistence service
*/
public static DecimalType deltaSince(Item item, AbstractInstant timestamp) {
return deltaSince(item, timestamp, getDefaultServiceId());
}
/**
* Gets the difference value of the state of a given <code>item</code> since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the average state value for
* @param timestamp the point in time from which to compute the delta
* @param serviceId the name of the {@link PersistenceService} to use
* @return the difference between now and then, or <code>null</code> if the given serviceId does not refer to an
* available {@link QueryablePersistenceService}, or if there is no persisted state for the given
* <code>item</code> at the given <code>timestamp</code> using the persistence service named
* <code>serviceId</code>
*/
public static DecimalType deltaSince(Item item, AbstractInstant timestamp, String serviceId) {
HistoricItem itemThen = historicState(item, timestamp, serviceId);
if (itemThen != null) {
DecimalType valueThen = (DecimalType) itemThen.getState();
DecimalType valueNow = (DecimalType) item.getStateAs(DecimalType.class);
if ((valueThen != null) && (valueNow != null)) {
return new DecimalType(valueNow.toBigDecimal().subtract(valueThen.toBigDecimal()));
}
}
return null;
}
/**
* Gets the evolution rate of the state of a given <code>item</code> since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the evolution rate value for
* @param timestamp the point in time from which to compute the evolution rate
* @param serviceId the name of the {@link PersistenceService} to use
* @return the evolution rate in percent (positive and negative) between now and then, or <code>null</code> if
* there is no default persistence service available, the default persistence service is not a
* {@link QueryablePersistenceService}, or if there is no persisted state for the given <code>item</code> at
* the given <code>timestamp</code>, or if there is a state but it is zero (which would cause a
* divide-by-zero error)
*/
public static DecimalType evolutionRate(Item item, AbstractInstant timestamp) {
return evolutionRate(item, timestamp, getDefaultServiceId());
}
/**
* Gets the evolution rate of the state of a given <code>item</code> since a certain point in time.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the evolution rate value for
* @param timestamp the point in time from which to compute the evolution rate
* @param serviceId the name of the {@link PersistenceService} to use
* @return the evolution rate in percent (positive and negative) between now and then, or <code>null</code> if
* the persistence service given by serviceId is not available or is not a
* {@link QueryablePersistenceService}, or if there is no persisted state for the given
* <code>item</code> at the given <code>timestamp</code> using the persistence service given by
* <code>serviceId</code>, or if there is a state but it is zero (which would cause a divide-by-zero
* error)
*/
public static DecimalType evolutionRate(Item item, AbstractInstant timestamp, String serviceId) {
HistoricItem itemThen = historicState(item, timestamp, serviceId);
if (itemThen != null) {
DecimalType valueThen = (DecimalType) itemThen.getState();
DecimalType valueNow = (DecimalType) item.getStateAs(DecimalType.class);
if ((valueThen != null) && (valueThen.toBigDecimal().compareTo(BigDecimal.ZERO) != 0)
&& (valueNow != null)) {
// ((now - then) / then) * 100
return new DecimalType(valueNow.toBigDecimal().subtract(valueThen.toBigDecimal())
.divide(valueThen.toBigDecimal(), MathContext.DECIMAL64).movePointRight(2));
}
}
return null;
}
/**
* Returns the previous state of a given <code>item</code>.
*
* @param item the item to get the previous state value for
* @return the previous state or <code>null</code> if no previous state could be found, or if the default
* persistence service is not configured or does not refer to a {@link QueryablePersistenceService}
*/
public static HistoricItem previousState(Item item) {
return previousState(item, false);
}
/**
* Returns the previous state of a given <code>item</code>.
*
* @param item the item to get the previous state value for
* @param skipEqual if true, skips equal state values and searches the first state not equal the current state
* @return the previous state or <code>null</code> if no previous state could be found, or if the default
* persistence service is not configured or does not refer to a {@link QueryablePersistenceService}
*/
public static HistoricItem previousState(Item item, boolean skipEqual) {
return previousState(item, skipEqual, getDefaultServiceId());
}
/**
* Returns the previous state of a given <code>item</code>.
* The {@link PersistenceService} identified by the <code>serviceId</code> is used.
*
* @param item the item to get the previous state value for
* @param skipEqual if <code>true</code>, skips equal state values and searches the first state not equal the
* current state
* @param serviceId the name of the {@link PersistenceService} to use
* @return the previous state or <code>null</code> if no previous state could be found, or if the given
* <code>serviceId</code> is not available or does not refer to a {@link QueryablePersistenceService}
*/
public static HistoricItem previousState(Item item, boolean skipEqual, String serviceId) {
PersistenceService service = getService(serviceId);
if (service instanceof QueryablePersistenceService) {
QueryablePersistenceService qService = (QueryablePersistenceService) service;
FilterCriteria filter = new FilterCriteria();
filter.setItemName(item.getName());
filter.setOrdering(Ordering.DESCENDING);
filter.setPageSize(skipEqual ? 1000 : 1);
int startPage = 0;
filter.setPageNumber(startPage);
Iterable<HistoricItem> items = qService.query(filter);
while (items != null) {
Iterator<HistoricItem> itemIterator = items.iterator();
int itemCount = 0;
while (itemIterator.hasNext()) {
HistoricItem historicItem = itemIterator.next();
itemCount++;
if (!skipEqual || (skipEqual && !historicItem.getState().equals(item.getState()))) {
return historicItem;
}
}
if (itemCount == filter.getPageSize()) {
filter.setPageNumber(++startPage);
items = qService.query(filter);
} else {
items = null;
}
}
return null;
} else {
LoggerFactory.getLogger(PersistenceExtensions.class)
.warn("There is no queryable persistence service registered with the id '{}'", serviceId);
return null;
}
}
}