mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[inmemory] Initial contribution (#15063)
This is the initial contribution of a new volatile persistence service. It does store values in memory only and can especially be used for forecasts or other data where volatile storage is sufficient. Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
parent
8a67d0ad94
commit
0b6bdad557
@ -1961,6 +1961,11 @@
|
||||
<artifactId>org.openhab.persistence.influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.persistence.inmemory</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.persistence.jdbc</artifactId>
|
||||
|
13
bundles/org.openhab.persistence.inmemory/NOTICE
Normal file
13
bundles/org.openhab.persistence.inmemory/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
14
bundles/org.openhab.persistence.inmemory/README.md
Normal file
14
bundles/org.openhab.persistence.inmemory/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# InMemory Persistence
|
||||
|
||||
The InMemory persistence service provides a volatile storage, i.e. it is cleared on shutdown.
|
||||
Because of that the `restoreOnStartup` strategy is not supported for this service.
|
||||
|
||||
The main use-case is to store data that is needed during runtime, e.g. temporary storage of forecast data that is retrieved from a binding.
|
||||
|
||||
Since all data is stored in memory only, there is no default strategy for this service.
|
||||
Unlike other persistence services, you MUST add a configuration, otherwise no data will be persisted.
|
||||
To avoid excessive memory usage, it is recommended to persist only a limited number of items and use a strategy that stores only data that is actually needed.
|
||||
|
||||
The service has a global configuration option `maxEntries` to limit the number of datapoints per item, the default value is `512`.
|
||||
When the number of datapoints is reached and a new value is persisted, the oldest (by timestamp) value will be removed.
|
||||
A `maxEntries` value of `0` disables automatic purging.
|
17
bundles/org.openhab.persistence.inmemory/pom.xml
Normal file
17
bundles/org.openhab.persistence.inmemory/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.persistence.inmemory</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Persistence Service :: InMemory</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.persistence.inmemory-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-persistence-inmemory" description="InMemory Persistence" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.persistence.inmemory/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
</features>
|
@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.inmemory.internal;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.core.ConfigParser;
|
||||
import org.openhab.core.config.core.ConfigurableService;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.persistence.FilterCriteria;
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.persistence.ModifiablePersistenceService;
|
||||
import org.openhab.core.persistence.PersistenceItemInfo;
|
||||
import org.openhab.core.persistence.PersistenceService;
|
||||
import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This is the implementation of the volatile {@link PersistenceService}.
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = { PersistenceService.class,
|
||||
ModifiablePersistenceService.class }, configurationPid = "org.openhab.inmemory", //
|
||||
property = Constants.SERVICE_PID + "=org.openhab.inmemory")
|
||||
@ConfigurableService(category = "persistence", label = "InMemory Persistence Service", description_uri = InMemoryPersistenceService.CONFIG_URI)
|
||||
public class InMemoryPersistenceService implements ModifiablePersistenceService {
|
||||
|
||||
private static final String SERVICE_ID = "inmemory";
|
||||
private static final String SERVICE_LABEL = "In Memory";
|
||||
|
||||
protected static final String CONFIG_URI = "persistence:inmemory";
|
||||
private final String MAX_ENTRIES_CONFIG = "maxEntries";
|
||||
private final long MAX_ENTRIES_DEFAULT = 512;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(InMemoryPersistenceService.class);
|
||||
|
||||
private final Map<String, PersistItem> persistMap = new ConcurrentHashMap<>();
|
||||
private long maxEntries = MAX_ENTRIES_DEFAULT;
|
||||
|
||||
@Activate
|
||||
public void activate(Map<String, Object> config) {
|
||||
modified(config);
|
||||
logger.debug("InMemory persistence service is now activated.");
|
||||
}
|
||||
|
||||
@Modified
|
||||
public void modified(Map<String, Object> config) {
|
||||
maxEntries = ConfigParser.valueAsOrElse(config.get(MAX_ENTRIES_CONFIG), Long.class, MAX_ENTRIES_DEFAULT);
|
||||
|
||||
persistMap.values().forEach(persistItem -> {
|
||||
Lock lock = persistItem.lock();
|
||||
lock.lock();
|
||||
try {
|
||||
while (persistItem.database().size() > maxEntries) {
|
||||
persistItem.database().pollFirst();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
logger.debug("InMemory persistence service deactivated.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return SERVICE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(@Nullable Locale locale) {
|
||||
return SERVICE_LABEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PersistenceItemInfo> getItemInfo() {
|
||||
return persistMap.entrySet().stream().map(this::toItemInfo).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Item item) {
|
||||
internalStore(item.getName(), ZonedDateTime.now(), item.getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Item item, @Nullable String alias) {
|
||||
String finalName = Objects.requireNonNullElse(alias, item.getName());
|
||||
internalStore(finalName, ZonedDateTime.now(), item.getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Item item, ZonedDateTime date, State state) {
|
||||
internalStore(item.getName(), date, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(FilterCriteria filter) throws IllegalArgumentException {
|
||||
String itemName = filter.getItemName();
|
||||
if (itemName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PersistItem persistItem = persistMap.get(itemName);
|
||||
if (persistItem == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Lock lock = persistItem.lock();
|
||||
lock.lock();
|
||||
try {
|
||||
List<PersistEntry> toRemove = persistItem.database().stream().filter(e -> applies(e, filter)).toList();
|
||||
toRemove.forEach(persistItem.database()::remove);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<HistoricItem> query(FilterCriteria filter) {
|
||||
String itemName = filter.getItemName();
|
||||
if (itemName == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
PersistItem persistItem = persistMap.get(itemName);
|
||||
if (persistItem == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
Lock lock = persistItem.lock();
|
||||
lock.lock();
|
||||
try {
|
||||
return persistItem.database().stream().filter(e -> applies(e, filter)).map(e -> toHistoricItem(itemName, e))
|
||||
.toList();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistenceStrategy> getDefaultStrategies() {
|
||||
// persist nothing by default
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private PersistenceItemInfo toItemInfo(Map.Entry<String, PersistItem> itemEntry) {
|
||||
Lock lock = itemEntry.getValue().lock();
|
||||
lock.lock();
|
||||
try {
|
||||
String name = itemEntry.getKey();
|
||||
Integer count = itemEntry.getValue().database().size();
|
||||
Instant earliest = itemEntry.getValue().database().first().timestamp().toInstant();
|
||||
Instant latest = itemEntry.getValue().database.last().timestamp.toInstant();
|
||||
return new PersistenceItemInfo() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Date getEarliest() {
|
||||
return Date.from(earliest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Date getLatest() {
|
||||
return Date.from(latest);
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private HistoricItem toHistoricItem(String itemName, PersistEntry entry) {
|
||||
return new HistoricItem() {
|
||||
@Override
|
||||
public ZonedDateTime getTimestamp() {
|
||||
return entry.timestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return entry.state();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return itemName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void internalStore(String itemName, ZonedDateTime timestamp, State state) {
|
||||
if (state instanceof UnDefType) {
|
||||
return;
|
||||
}
|
||||
|
||||
PersistItem persistItem = Objects.requireNonNull(persistMap.computeIfAbsent(itemName,
|
||||
k -> new PersistItem(new TreeSet<>(Comparator.comparing(PersistEntry::timestamp)),
|
||||
new ReentrantLock())));
|
||||
|
||||
Lock lock = persistItem.lock();
|
||||
lock.lock();
|
||||
try {
|
||||
persistItem.database().add(new PersistEntry(timestamp, state));
|
||||
|
||||
while (persistItem.database.size() > maxEntries) {
|
||||
persistItem.database().pollFirst();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawType", "unchecked" })
|
||||
private boolean applies(PersistEntry entry, FilterCriteria filter) {
|
||||
ZonedDateTime beginDate = filter.getBeginDate();
|
||||
if (beginDate != null && entry.timestamp().isBefore(beginDate)) {
|
||||
return false;
|
||||
}
|
||||
ZonedDateTime endDate = filter.getEndDate();
|
||||
if (endDate != null && entry.timestamp().isAfter(endDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
State refState = filter.getState();
|
||||
FilterCriteria.Operator operator = filter.getOperator();
|
||||
if (refState == null) {
|
||||
// no state filter
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operator == FilterCriteria.Operator.EQ) {
|
||||
return entry.state().equals(refState);
|
||||
}
|
||||
|
||||
if (operator == FilterCriteria.Operator.NEQ) {
|
||||
return !entry.state().equals(refState);
|
||||
}
|
||||
|
||||
if (entry.state() instanceof Comparable comparableState && entry.state.getClass().equals(refState.getClass())) {
|
||||
if (operator == FilterCriteria.Operator.GT) {
|
||||
return comparableState.compareTo(refState) > 0;
|
||||
}
|
||||
if (operator == FilterCriteria.Operator.GTE) {
|
||||
return comparableState.compareTo(refState) >= 0;
|
||||
}
|
||||
if (operator == FilterCriteria.Operator.LT) {
|
||||
return comparableState.compareTo(refState) < 0;
|
||||
}
|
||||
if (operator == FilterCriteria.Operator.LTE) {
|
||||
return comparableState.compareTo(refState) <= 0;
|
||||
}
|
||||
} else {
|
||||
logger.warn("Using operator {} but state {} is not comparable!", operator, refState);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private record PersistEntry(ZonedDateTime timestamp, State state) {
|
||||
};
|
||||
|
||||
private record PersistItem(TreeSet<PersistEntry> database, Lock lock) {
|
||||
};
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="inmemory" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>persistence</type>
|
||||
<name>InMemory Persistence</name>
|
||||
<description>A volatile persistence service to temporarily store data.</description>
|
||||
<connection>none</connection>
|
||||
|
||||
<service-id>org.openhab.inmemory</service-id>
|
||||
|
||||
<config-description>
|
||||
<parameter name="maxEntries" type="integer" min="0">
|
||||
<label>Maximum Entries</label>
|
||||
<description>The maximum number of values stored for each item (0 = infinite).</description>
|
||||
<default>512</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.persistence.inmemory.internal;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.persistence.FilterCriteria;
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* The {@link InMemoryPersistenceTests} contains tests for the {@link InMemoryPersistenceService}
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@NonNullByDefault
|
||||
public class InMemoryPersistenceTests {
|
||||
private static final String ITEM_NAME = "testItem";
|
||||
private static final String ALIAS = "alias";
|
||||
|
||||
private @NonNullByDefault({}) InMemoryPersistenceService service;
|
||||
private @NonNullByDefault({}) @Mock GenericItem item;
|
||||
|
||||
private @NonNullByDefault({}) FilterCriteria filterCriteria;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
when(item.getName()).thenReturn(ITEM_NAME);
|
||||
|
||||
filterCriteria = new FilterCriteria();
|
||||
filterCriteria.setItemName(ITEM_NAME);
|
||||
|
||||
service = new InMemoryPersistenceService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeDirect() {
|
||||
State state = new DecimalType(1);
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
ZonedDateTime expectedTime = ZonedDateTime.now();
|
||||
service.store(item);
|
||||
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, hasSize(1));
|
||||
assertThat(storedStates.first().getName(), is(ITEM_NAME));
|
||||
assertThat(storedStates.first().getState(), is(state));
|
||||
assertThat((double) storedStates.first().getTimestamp().toEpochSecond(),
|
||||
is(closeTo(expectedTime.toEpochSecond(), 2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeAlias() {
|
||||
State state = new PercentType(1);
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
ZonedDateTime expectedTime = ZonedDateTime.now();
|
||||
service.store(item, ALIAS);
|
||||
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
|
||||
// query with item name should return nothing
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
assertThat(storedStates, is(empty()));
|
||||
|
||||
filterCriteria.setItemName(ALIAS);
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates.size(), is(1));
|
||||
assertThat(storedStates.first().getName(), is(ALIAS));
|
||||
assertThat(storedStates.first().getState(), is(state));
|
||||
assertThat((double) storedStates.first().getTimestamp().toEpochSecond(),
|
||||
is(closeTo(expectedTime.toEpochSecond(), 2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeHistoric() {
|
||||
State state = new HSBType("120,100,100");
|
||||
when(item.getState()).thenReturn(state);
|
||||
|
||||
State historicState = new HSBType("40,50,50");
|
||||
ZonedDateTime expectedTime = ZonedDateTime.of(2022, 05, 31, 10, 0, 0, 0, ZoneId.systemDefault());
|
||||
service.store(item, expectedTime, historicState);
|
||||
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, hasSize(1));
|
||||
assertThat(storedStates.first().getName(), is(ITEM_NAME));
|
||||
assertThat(storedStates.first().getState(), is(historicState));
|
||||
assertThat(storedStates.first().getTimestamp(), is(expectedTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryWithoutItemNameReturnsEmptyList() {
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
service.query(new FilterCriteria()).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryUnknownItemReturnsEmptyList() {
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeBetweenTimes() {
|
||||
State historicState1 = new StringType("value1");
|
||||
State historicState2 = new StringType("value2");
|
||||
State historicState3 = new StringType("value3");
|
||||
|
||||
ZonedDateTime expectedTime = ZonedDateTime.of(2022, 05, 31, 10, 0, 0, 0, ZoneId.systemDefault());
|
||||
service.store(item, expectedTime, historicState1);
|
||||
service.store(item, expectedTime.plusHours(2), historicState2);
|
||||
service.store(item, expectedTime.plusHours(4), historicState3);
|
||||
|
||||
// ensure both are stored
|
||||
TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, hasSize(3));
|
||||
|
||||
filterCriteria.setBeginDate(expectedTime.plusHours(1));
|
||||
filterCriteria.setEndDate(expectedTime.plusHours(3));
|
||||
service.remove(filterCriteria);
|
||||
|
||||
filterCriteria = new FilterCriteria();
|
||||
filterCriteria.setItemName(ITEM_NAME);
|
||||
storedStates.clear();
|
||||
service.query(filterCriteria).forEach(storedStates::add);
|
||||
|
||||
assertThat(storedStates, hasSize(2));
|
||||
|
||||
assertThat(storedStates.first().getName(), is(ITEM_NAME));
|
||||
assertThat(storedStates.first().getState(), is(historicState1));
|
||||
assertThat(storedStates.first().getTimestamp(), is(expectedTime));
|
||||
|
||||
assertThat(storedStates.last().getName(), is(ITEM_NAME));
|
||||
assertThat(storedStates.last().getState(), is(historicState3));
|
||||
assertThat(storedStates.last().getTimestamp(), is(expectedTime.plusHours(4)));
|
||||
}
|
||||
}
|
@ -423,6 +423,7 @@
|
||||
<!-- persistence -->
|
||||
<module>org.openhab.persistence.dynamodb</module>
|
||||
<module>org.openhab.persistence.influxdb</module>
|
||||
<module>org.openhab.persistence.inmemory</module>
|
||||
<module>org.openhab.persistence.jdbc</module>
|
||||
<module>org.openhab.persistence.jpa</module>
|
||||
<module>org.openhab.persistence.mapdb</module>
|
||||
|
Loading…
Reference in New Issue
Block a user