diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java index 66a0578f2d6..4972b55d692 100644 --- a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java @@ -18,6 +18,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; import javax.annotation.security.RolesAllowed; import javax.ws.rs.Consumes; @@ -179,9 +180,10 @@ public Response httpGetPersistenceServiceConfiguration(@Context HttpHeaders head PersistenceService service = persistenceServiceRegistry.get(serviceId); if (service != null) { List strategies = service.getDefaultStrategies(); - List configs = List.of( - new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null)); - configuration = new PersistenceServiceConfiguration(serviceId, configs, strategies, strategies, + List configs = List + .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null)); + Map aliases = Map.of(); + configuration = new PersistenceServiceConfiguration(serviceId, configs, aliases, strategies, strategies, List.of()); editable = true; } @@ -363,6 +365,9 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, // If serviceId is null, then use the default service PersistenceService service; String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId(); + if (effectiveServiceId == null) { + return null; + } service = persistenceServiceRegistry.get(effectiveServiceId); if (service == null) { @@ -411,6 +416,8 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, ItemHistoryDTO dto = new ItemHistoryDTO(); dto.name = itemName; + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId); + String alias = config != null ? config.getAliases().get(itemName) : null; // If "boundary" is true then we want to get one value before and after the requested period // This is necessary for values that don't change often otherwise data will start after the start of the graph @@ -422,7 +429,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filterBeforeStart.setEndDate(dateTimeBegin); filterBeforeStart.setPageSize(1); filterBeforeStart.setOrdering(Ordering.DESCENDING); - result = qService.query(filterBeforeStart); + result = qService.query(filterBeforeStart, alias); if (result.iterator().hasNext()) { dto.addData(dateTimeBegin.toInstant().toEpochMilli(), result.iterator().next().getState()); quantity++; @@ -441,7 +448,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filter.setBeginDate(dateTimeBegin); filter.setEndDate(dateTimeEnd); filter.setOrdering(Ordering.ASCENDING); - result = qService.query(filter); + result = qService.query(filter, alias); Iterator it = result.iterator(); // Iterate through the data @@ -472,7 +479,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filterAfterEnd.setBeginDate(dateTimeEnd); filterAfterEnd.setPageSize(1); filterAfterEnd.setOrdering(Ordering.ASCENDING); - result = qService.query(filterAfterEnd); + result = qService.query(filterAfterEnd, alias); if (result.iterator().hasNext()) { dto.addData(dateTimeEnd.toInstant().toEpochMilli(), result.iterator().next().getState()); quantity++; @@ -570,13 +577,15 @@ private Response deletePersistenceItemData(@Nullable String serviceId, String it // This is necessary for values that don't change often otherwise data will start after the start of the graph // (or not at all if there's no change during the graph period) FilterCriteria filter = new FilterCriteria(); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(serviceId); + String alias = config != null ? config.getAliases().get(itemName) : null; filter.setItemName(itemName); filter.setBeginDate(dateTimeBegin); filter.setEndDate(dateTimeEnd); ModifiablePersistenceService mService = (ModifiablePersistenceService) service; try { - mService.remove(filter); + mService.remove(filter, alias); } catch (IllegalArgumentException e) { return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Invalid filter parameters."); } @@ -589,7 +598,7 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId(); PersistenceService service = persistenceServiceRegistry.get(effectiveServiceId); - if (service == null) { + if (effectiveServiceId == null || service == null) { logger.warn("Persistence service not found '{}'.", effectiveServiceId); return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Persistence service not found: " + effectiveServiceId); @@ -627,7 +636,9 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin } ModifiablePersistenceService mService = (ModifiablePersistenceService) service; - mService.store(item, dateTime, state); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId); + String alias = config != null ? config.getAliases().get(itemName) : null; + mService.store(item, dateTime, state, alias); persistenceManager.handleExternalPersistenceDataChange(mService, item); diff --git a/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java b/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java index b2fd7b38ef0..fa4863f8956 100644 --- a/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java +++ b/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java @@ -16,7 +16,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.time.ZoneId; @@ -108,7 +108,7 @@ public String getName() { }); } - when(pServiceMock.query(any())).thenReturn(items); + when(pServiceMock.query(any(), any())).thenReturn(items); when(persistenceServiceRegistryMock.get(PERSISTENCE_SERVICE_ID)).thenReturn(pServiceMock); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault()); diff --git a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext index 7699c6e7405..45c664bac9c 100644 --- a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext +++ b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext @@ -11,6 +11,7 @@ PersistenceModel: '}' ('Filters' '{' filters+=Filter* '}')? ('Items' '{' configs+=PersistenceConfiguration* '}')? + ('Aliases' '{' aliases+=AliasConfiguration* '}')? ; Strategy: @@ -56,7 +57,7 @@ NotIncludeFilter: PersistenceConfiguration: - items+=(AllConfig | ItemConfig | GroupConfig) (',' items+=(AllConfig | ItemConfig | GroupConfig))* ('->' alias=STRING)? + items+=(AllConfig | ItemConfig | GroupConfig) (',' items+=(AllConfig | ItemConfig | GroupConfig))* ((':' ('strategy' '=' strategies+=[Strategy|ID] (',' strategies+=[Strategy|ID])*)? ('filter' '=' filters+=[Filter|ID] (',' filters+=[Filter|ID])*)?) | ';') @@ -75,6 +76,11 @@ GroupConfig: group=ID '*' ; + +AliasConfiguration: + item=ID '->' alias=STRING +; + DECIMAL returns ecore::EBigDecimal : INT ('.' INT)? ; diff --git a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java index 60fb35d992f..5d11f74952d 100644 --- a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java +++ b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java @@ -13,6 +13,7 @@ package org.openhab.core.model.persistence.internal; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -24,6 +25,7 @@ import org.openhab.core.model.core.EventType; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.model.core.ModelRepositoryChangeListener; +import org.openhab.core.model.persistence.persistence.AliasConfiguration; import org.openhab.core.model.persistence.persistence.AllConfig; import org.openhab.core.model.persistence.persistence.CronStrategy; import org.openhab.core.model.persistence.persistence.EqualsFilter; @@ -67,6 +69,7 @@ * @author Kai Kreuzer - Initial contribution * @author Markus Rathgeb - Move non-model logic to core.persistence * @author Jan N. Klug - Refactored to {@link PersistenceServiceConfigurationProvider} + * @author Mark Herwege - Separate alias handling */ @Component(immediate = true, service = PersistenceServiceConfigurationProvider.class) @NonNullByDefault @@ -98,14 +101,17 @@ public void modelChanged(String modelName, EventType type) { String serviceName = serviceName(modelName); if (type == EventType.REMOVED) { PersistenceServiceConfiguration removed = configurations.remove(serviceName); - notifyListenersAboutRemovedElement(removed); + if (removed != null) { + notifyListenersAboutRemovedElement(removed); + } } else { final PersistenceModel model = (PersistenceModel) modelRepository.getModel(modelName); if (model != null) { PersistenceServiceConfiguration newConfiguration = new PersistenceServiceConfiguration(serviceName, - mapConfigs(model.getConfigs()), mapStrategies(model.getDefaults()), - mapStrategies(model.getStrategies()), mapFilters(model.getFilters())); + mapConfigs(model.getConfigs()), mapAliases(model.getAliases()), + mapStrategies(model.getDefaults()), mapStrategies(model.getStrategies()), + mapFilters(model.getFilters())); PersistenceServiceConfiguration oldConfiguration = configurations.put(serviceName, newConfiguration); if (oldConfiguration == null) { @@ -155,10 +161,18 @@ private PersistenceItemConfiguration mapConfig(PersistenceConfiguration config) items.add(new PersistenceItemConfig(itemConfig.getItem())); } } - return new PersistenceItemConfiguration(items, config.getAlias(), mapStrategies(config.getStrategies()), + return new PersistenceItemConfiguration(items, mapStrategies(config.getStrategies()), mapFilters(config.getFilters())); } + private Map mapAliases(List aliases) { + final Map map = new HashMap<>(); + for (final AliasConfiguration alias : aliases) { + map.put(alias.getItem(), alias.getAlias()); + } + return map; + } + private List mapStrategies(List strategies) { final List lst = new LinkedList<>(); for (final Strategy strategy : strategies) { diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java index 6948bc55fcf..e06c5a40b40 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java @@ -70,10 +70,29 @@ public interface ModifiablePersistenceService extends QueryablePersistenceServic * Removes data associated with an item from a persistence service. * If all data is removed for the specified item, the persistence service should free any resources associated with * the item (e.g. remove any tables or delete files from the storage). + * If the persistence service implementing this method supports aliases for item names, the default implementation + * of {@link #remove(FilterCriteria, String)} should be overriden as well. * * @param filter the filter to apply to the data removal. ItemName can not be null. * @return true if the query executed successfully * @throws IllegalArgumentException if item name is null. */ boolean remove(FilterCriteria filter) throws IllegalArgumentException; + + /** + * Removes data associated with an item from a persistence service. + * If all data is removed for the specified item, the persistence service should free any resources associated with + * the item (e.g. remove any tables or delete files from the storage). + * Persistence services supporting aliases should override the default implementation from this interface that + * ignores aliases when querying. + * + * @param filter the filter to apply to the data removal. ItemName can not be null. + * @param alias for item name in database + * @return true if the query executed successfully + * @throws IllegalArgumentException if item name is null. + */ + default boolean remove(FilterCriteria filter, @Nullable String alias) throws IllegalArgumentException { + // Default implementation ignores alias + return remove(filter); + } } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java index b1d4fa50ca8..00aefb71ceb 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java @@ -25,15 +25,15 @@ * This class holds the configuration of a persistence strategy for specific items. * * @author Markus Rathgeb - Initial contribution + * @author Mark Herwege - extract alias configuration */ @NonNullByDefault -public record PersistenceItemConfiguration(List items, @Nullable String alias, - List strategies, List filters) { +public record PersistenceItemConfiguration(List items, List strategies, + List filters) { - public PersistenceItemConfiguration(final List items, @Nullable final String alias, + public PersistenceItemConfiguration(final List items, @Nullable final List strategies, @Nullable final List filters) { this.items = items; - this.alias = alias; this.strategies = Objects.requireNonNullElse(strategies, List.of()); this.filters = Objects.requireNonNullElse(filters, List.of()); } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java index 4b917056e37..59ada1e8320 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java @@ -15,6 +15,7 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; /** * A queryable persistence service which can be used to store and retrieve @@ -28,17 +29,36 @@ public interface QueryablePersistenceService extends PersistenceService { /** * Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}. + * If the persistence service implementing this class supports using aliases for item names, the default + * implementation of {@link #query(FilterCriteria, String)} should be overriden as well. * * @param filter the filter to apply to the query * @return a time series of items */ Iterable query(FilterCriteria filter); + /** + * Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}. + * If the persistence service implementing this interface supports aliases, the default implementation should be + * overriden to query the database with the aliased name. + * + * @param filter the filter to apply to the query + * @param alias for item name in database + * @return a time series of items + */ + default Iterable query(FilterCriteria filter, @Nullable String alias) { + // Default implementation ignores alias + return query(filter); + } + /** * Returns a set of {@link PersistenceItemInfo} about items that are stored in the persistence service. This allows * the persistence service to return information about items that are no long available as an - * {@link org.openhab.core.items.Item} in - * openHAB. If it is not possible to retrieve the information an empty set should be returned. + * {@link org.openhab.core.items.Item} in openHAB. If it is not possible to retrieve the information an empty set + * should be returned. + * + * Note that implementations of this method may return an alias for an existing item if the database does not store + * the mapping between item name and alias. * * @return a set of information about the persisted items */ diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java index 3d4687aa26d..88567a471d9 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java @@ -16,7 +16,6 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** * The {@link org.openhab.core.persistence.dto.PersistenceItemConfigurationDTO} is used for transferring persistence @@ -29,5 +28,4 @@ public class PersistenceItemConfigurationDTO { public Collection items = List.of(); public Collection strategies = List.of(); public Collection filters = List.of(); - public @Nullable String alias; } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java index 505ead797ea..0f148de38f6 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -26,6 +27,7 @@ public class PersistenceServiceConfigurationDTO { public String serviceId = ""; public Collection configs = List.of(); + public Map aliases = Map.of(); public Collection defaults = List.of(); public Collection cronStrategies = List.of(); public Collection thresholdFilters = List.of(); diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java index d7b5bed0295..cd84bcd4196 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java @@ -41,6 +41,8 @@ import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; import org.openhab.core.persistence.QueryablePersistenceService; +import org.openhab.core.persistence.registry.PersistenceServiceConfiguration; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; import org.openhab.core.types.TimeSeries; import org.openhab.core.types.TypeParser; @@ -72,12 +74,15 @@ public class PersistenceExtensions { private static @Nullable PersistenceServiceRegistry registry; + private static @Nullable PersistenceServiceConfigurationRegistry configRegistry; private static @Nullable TimeZoneProvider timeZoneProvider; @Activate public PersistenceExtensions(@Reference PersistenceServiceRegistry registry, + @Reference PersistenceServiceConfigurationRegistry configRegistry, @Reference TimeZoneProvider timeZoneProvider) { PersistenceExtensions.registry = registry; + PersistenceExtensions.configRegistry = configRegistry; PersistenceExtensions.timeZoneProvider = timeZoneProvider; } @@ -108,7 +113,7 @@ private static void internalPersist(Item item, @Nullable String serviceId) { } PersistenceService service = getService(effectiveServiceId); if (service != null) { - service.store(item); + service.store(item, getAlias(item, effectiveServiceId)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -147,7 +152,7 @@ private static void internalPersist(Item item, ZonedDateTime timestamp, State st } PersistenceService service = getService(effectiveServiceId); if (service instanceof ModifiablePersistenceService modifiableService) { - modifiableService.store(item, timestamp, state); + modifiableService.store(item, timestamp, state, getAlias(item, effectiveServiceId)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -225,8 +230,9 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable internalRemoveAllStatesBetween(item, timeSeries.getBegin().atZone(timeZone), timeSeries.getEnd().atZone(timeZone), serviceId); } + String alias = getAlias(item, effectiveServiceId); timeSeries.getStates() - .forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state())); + .forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state(), alias)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -315,10 +321,11 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); filter.setEndDate(timestamp); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setPageSize(1); filter.setOrdering(Ordering.DESCENDING); - Iterable result = qService.query(filter); + Iterable result = qService.query(filter, alias); if (result.iterator().hasNext()) { return result.iterator().next(); } @@ -448,6 +455,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable PersistenceService service = getService(effectiveServiceId); if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); if (forward) { filter.setBeginDate(ZonedDateTime.now()); @@ -460,7 +468,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable int startPage = 0; filter.setPageNumber(startPage); - Iterable items = qService.query(filter); + Iterable items = qService.query(filter, alias); Iterator itemIterator = items.iterator(); State state = item.getState(); if (itemIterator.hasNext()) { @@ -491,7 +499,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } if (itemCount == filter.getPageSize()) { filter.setPageNumber(++startPage); - items = qService.query(filter); + items = qService.query(filter, alias); itemCount = 0; } else { items = null; @@ -617,6 +625,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable PersistenceService service = getService(effectiveServiceId); if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); if (forward) { filter.setBeginDate(ZonedDateTime.now()); @@ -629,7 +638,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable int startPage = 0; filter.setPageNumber(startPage); - Iterable items = qService.query(filter); + Iterable items = qService.query(filter, alias); while (items != null) { Iterator itemIterator = items.iterator(); int itemCount = 0; @@ -642,7 +651,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } if (itemCount == filter.getPageSize()) { filter.setPageNumber(++startPage); - items = qService.query(filter); + items = qService.query(filter, alias); } else { items = null; } @@ -2532,10 +2541,11 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } else { filter.setEndDate(ZonedDateTime.now()); } + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setOrdering(Ordering.ASCENDING); - return qService.query(filter); + return qService.query(filter, alias); } else { LoggerFactory.getLogger(PersistenceExtensions.class) .warn("There is no queryable persistence service registered with the id '{}'", effectiveServiceId); @@ -2648,10 +2658,11 @@ private static void internalRemoveAllStatesBetween(Item item, @Nullable ZonedDat } else { filter.setEndDate(ZonedDateTime.now()); } + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setOrdering(Ordering.ASCENDING); - mService.remove(filter); + mService.remove(filter, alias); } else { LoggerFactory.getLogger(PersistenceExtensions.class) .warn("There is no modifiable persistence service registered with the id '{}'", effectiveServiceId); @@ -2729,6 +2740,15 @@ private static void internalRemoveAllStatesBetween(Item item, @Nullable ZonedDat return null; } + private static @Nullable String getAlias(Item item, String serviceId) { + PersistenceServiceConfigurationRegistry reg = configRegistry; + if (reg != null) { + PersistenceServiceConfiguration config = reg.get(serviceId); + return config != null ? config.getAliases().get(item.getName()) : null; + } + return null; + } + private static @Nullable DecimalType getItemValue(Item item) { Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; if (baseItem instanceof NumberItem numberItem) { diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java index e09678b3224..2f4ec883e45 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java @@ -13,9 +13,7 @@ package org.openhab.core.persistence.internal; import static org.openhab.core.persistence.FilterCriteria.Ordering.ASCENDING; -import static org.openhab.core.persistence.strategy.PersistenceStrategy.Globals.FORECAST; -import static org.openhab.core.persistence.strategy.PersistenceStrategy.Globals.RESTORE; -import static org.openhab.core.persistence.strategy.PersistenceStrategy.Globals.UPDATE; +import static org.openhab.core.persistence.strategy.PersistenceStrategy.Globals.*; import java.time.Instant; import java.time.ZoneId; @@ -186,7 +184,7 @@ private void handleStateEvent(Item item, boolean changed) { .filter(itemConfig -> itemConfig.filters().stream().allMatch(filter -> filter.apply(item))) .forEach(itemConfig -> { itemConfig.filters().forEach(filter -> filter.persisted(item)); - container.getPersistenceService().store(item, itemConfig.alias()); + container.getPersistenceService().store(item, container.getAlias(item)); })); } @@ -323,7 +321,7 @@ public void timeSeriesUpdated(Item item, TimeSeries timeSeries) { ZonedDateTime end = timeSeries.getEnd().atZone(ZoneId.systemDefault()); FilterCriteria removeFilter = new FilterCriteria().setItemName(item.getName()) .setBeginDate(begin).setEndDate(end); - service.remove(removeFilter); + service.remove(removeFilter, container.getAlias(item)); ScheduledCompletableFuture forecastJob = container.forecastJobs.get(item.getName()); if (forecastJob != null && forecastJob.getScheduledTime().isAfter(begin) && forecastJob.getScheduledTime().isBefore(end)) { @@ -448,12 +446,17 @@ public Stream getMatchingConfigurations(Persistenc }).stream()); } + public @Nullable String getAlias(Item item) { + return configuration.getAliases().get(item.getName()); + } + private PersistenceServiceConfiguration getDefaultConfig() { List strategies = persistenceService.getDefaultStrategies(); List configs = List - .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null)); - return new PersistenceServiceConfiguration(persistenceService.getId(), configs, strategies, strategies, - List.of()); + .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null)); + Map aliases = Map.of(); + return new PersistenceServiceConfiguration(persistenceService.getId(), configs, aliases, strategies, + strategies, List.of()); } /** @@ -520,6 +523,7 @@ public void removeItem(String itemName) { private void restoreItemStateIfPossible(Item item) { QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService; + String alias = getAlias(item); FilterCriteria filter = new FilterCriteria().setItemName(item.getName()).setEndDate(ZonedDateTime.now()) .setPageSize(1); @@ -530,7 +534,7 @@ private void restoreItemStateIfPossible(Item item) { .onException(e -> logger.error( "Exception occurred while querying persistence service '{}' to restore '{}': {}", queryService.getId(), item.getName(), e.getMessage(), e)) - .build().query(filter); + .build().query(filter, alias); if (result == null) { // in case of an exception or timeout, the safe caller returns null return; @@ -566,6 +570,7 @@ public void scheduleNextForecastForItem(String itemName, Instant time, State sta public void scheduleNextPersistedForecastForItem(String itemName) { Item item = itemRegistry.get(itemName); if (item instanceof GenericItem) { + String alias = getAlias(item); QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService; FilterCriteria filter = new FilterCriteria().setItemName(itemName).setBeginDate(ZonedDateTime.now()) .setOrdering(ASCENDING); @@ -574,7 +579,7 @@ public void scheduleNextPersistedForecastForItem(String itemName) { queryService.getId(), SafeCaller.DEFAULT_TIMEOUT)) .onException(e -> logger.error("Exception occurred while querying persistence service '{}': {}", queryService.getId(), e.getMessage(), e)) - .build().query(filter).iterator(); + .build().query(filter, alias).iterator(); while (result.hasNext()) { HistoricItem next = result.next(); if (next.getTimestamp().isAfter(ZonedDateTime.now())) { @@ -599,7 +604,7 @@ private void persistJob(List itemConfigs) { if (itemConfig.filters().stream().allMatch(filter -> filter.apply(item))) { long startTime = System.nanoTime(); itemConfig.filters().forEach(filter -> filter.persisted(item)); - persistenceService.store(item, itemConfig.alias()); + persistenceService.store(item, getAlias(item)); logger.trace("Storing item '{}' with persistence service '{}' took {}ms", item.getName(), configuration.getUID(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)); } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java index c40b1502580..661111b8e69 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.common.registry.Identifiable; @@ -30,15 +31,17 @@ public class PersistenceServiceConfiguration implements Identifiable { private final String serviceId; private final List configs; + private final Map aliases; private final List defaults; private final List strategies; private final List filters; public PersistenceServiceConfiguration(String serviceId, Collection configs, - Collection defaults, Collection strategies, - Collection filters) { + Map aliases, Collection defaults, + Collection strategies, Collection filters) { this.serviceId = serviceId; this.configs = List.copyOf(configs); + this.aliases = Map.copyOf(aliases); this.defaults = List.copyOf(defaults); this.strategies = List.copyOf(strategies); this.filters = List.copyOf(filters); @@ -58,6 +61,15 @@ public List getConfigs() { return configs; } + /** + * Get the item aliases. + * + * @return a map of items to aliases + */ + public Map getAliases() { + return aliases; + } + /** * Get the default strategies. * diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java index c075bed2af2..6a4e95a0d29 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java @@ -58,6 +58,7 @@ public static PersistenceServiceConfigurationDTO map( dto.serviceId = persistenceServiceConfiguration.getUID(); dto.configs = persistenceServiceConfiguration.getConfigs().stream() .map(PersistenceServiceConfigurationDTOMapper::mapPersistenceItemConfig).toList(); + dto.aliases = Map.copyOf(persistenceServiceConfiguration.getAliases()); dto.defaults = persistenceServiceConfiguration.getDefaults().stream().map(PersistenceStrategy::getName) .toList(); dto.cronStrategies = filterList(persistenceServiceConfiguration.getStrategies(), PersistenceCronStrategy.class, @@ -98,10 +99,12 @@ public static PersistenceServiceConfiguration map(PersistenceServiceConfiguratio .map(str -> stringToPersistenceStrategy(str, strategyMap, dto.serviceId)).toList(); List filters = config.filters.stream() .map(str -> stringToPersistenceFilter(str, filterMap, dto.serviceId)).toList(); - return new PersistenceItemConfiguration(items, config.alias, strategies, filters); + return new PersistenceItemConfiguration(items, strategies, filters); }).toList(); - return new PersistenceServiceConfiguration(dto.serviceId, configs, defaults, strategyMap.values(), + Map aliases = Map.copyOf(dto.aliases); + + return new PersistenceServiceConfiguration(dto.serviceId, configs, aliases, defaults, strategyMap.values(), filterMap.values()); } @@ -159,7 +162,6 @@ private static PersistenceItemConfigurationDTO mapPersistenceItemConfig(Persiste .toList(); itemDto.strategies = config.strategies().stream().map(PersistenceStrategy::getName).toList(); itemDto.filters = config.filters().stream().map(PersistenceFilter::getName).toList(); - itemDto.alias = config.alias(); return itemDto; } diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java index ab60d1dc3cb..691a52edae0 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java @@ -16,6 +16,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.openhab.core.persistence.extensions.TestPersistenceService.*; @@ -52,6 +53,7 @@ import org.openhab.core.persistence.HistoricItem; import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; /** @@ -79,6 +81,8 @@ public class PersistenceExtensionsTest { private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock; private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock; + private @Mock @NonNullByDefault({}) PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistryMock; + private @NonNullByDefault({}) GenericItem numberItem, quantityItem, groupQuantityItem, switchItem; @BeforeEach @@ -107,6 +111,7 @@ public void setUp() { when(itemRegistryMock.get(TEST_SWITCH)).thenReturn(switchItem); when(itemRegistryMock.get(TEST_GROUP_QUANTITY_NUMBER)).thenReturn(groupQuantityItem); + when(persistenceServiceConfigurationRegistryMock.get(anyString())).thenReturn(null); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault()); new PersistenceExtensions(new PersistenceServiceRegistry() { @@ -133,7 +138,7 @@ public Set getAll() { public @Nullable PersistenceService get(@Nullable String serviceId) { return TestPersistenceService.SERVICE_ID.equals(serviceId) ? testPersistenceService : null; } - }, timeZoneProviderMock); + }, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock); } @Test @@ -3375,7 +3380,7 @@ public Set getAll() { public @Nullable PersistenceService get(@Nullable String serviceId) { return TestCachedValuesPersistenceService.ID.equals(serviceId) ? persistenceService : null; } - }, timeZoneProviderMock); + }, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock); if (historicHours > 0) { ZonedDateTime beginHistory = now.minusHours(historicHours); diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java index bfd6ab7890c..eca27f7dbd1 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java @@ -153,7 +153,7 @@ public void setUp() throws ItemNotFoundException { when(itemRegistryMock.getItems()).thenReturn(List.of(TEST_ITEM, TEST_ITEM2, TEST_ITEM3, TEST_GROUP_ITEM)); when(persistenceServiceMock.getId()).thenReturn(TEST_PERSISTENCE_SERVICE_ID); when(queryablePersistenceServiceMock.getId()).thenReturn(TEST_QUERYABLE_PERSISTENCE_SERVICE_ID); - when(queryablePersistenceServiceMock.query(any())).thenReturn(List.of(TEST_HISTORIC_ITEM)); + when(queryablePersistenceServiceMock.query(any(), any())).thenReturn(List.of(TEST_HISTORIC_ITEM)); when(modifiablePersistenceServiceMock.getId()).thenReturn(TEST_MODIFIABLE_PERSISTENCE_SERVICE_ID); manager = new PersistenceManagerImpl(cronSchedulerMock, schedulerMock, itemRegistryMock, safeCallerMock, @@ -289,7 +289,7 @@ public void restoreOnStartupWhenItemNull() { assertThat(TEST_ITEM.getState(), is(TEST_STATE)); assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE)); - verify(queryablePersistenceServiceMock, times(3)).query(any()); + verify(queryablePersistenceServiceMock, times(3)).query(any(), any()); verifyNoMoreInteractions(queryablePersistenceServiceMock); verifyNoMoreInteractions(persistenceServiceMock); @@ -310,7 +310,7 @@ public void noRestoreOnStartupWhenItemNotNull() { assertThat(TEST_ITEM2.getState(), is(TEST_STATE)); assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE)); - verify(queryablePersistenceServiceMock, times(2)).query(any()); + verify(queryablePersistenceServiceMock, times(2)).query(any(), any()); verifyNoMoreInteractions(queryablePersistenceServiceMock); verifyNoMoreInteractions(persistenceServiceMock); @@ -482,7 +482,7 @@ private PersistenceServiceConfiguration addConfiguration(String serviceId, Persi PersistenceStrategy strategy, @Nullable PersistenceFilter filter) { List filters = filter != null ? List.of(filter) : List.of(); - PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(List.of(itemConfig), null, + PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(List.of(itemConfig), List.of(strategy), filters); List strategies = PersistenceStrategy.Globals.STRATEGIES.containsValue(strategy) @@ -490,7 +490,7 @@ private PersistenceServiceConfiguration addConfiguration(String serviceId, Persi : List.of(strategy); PersistenceServiceConfiguration serviceConfiguration = new PersistenceServiceConfiguration(serviceId, - List.of(itemConfiguration), List.of(), strategies, filters); + List.of(itemConfiguration), Map.of(), List.of(), strategies, filters); manager.added(serviceConfiguration); return serviceConfiguration; diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java index 98d97287c43..1853bc354d6 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java @@ -49,6 +49,8 @@ import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; import org.openhab.core.persistence.QueryablePersistenceService; +import org.openhab.core.persistence.registry.PersistenceServiceConfiguration; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; import org.openhab.core.ui.chart.ChartProvider; import org.openhab.core.ui.internal.chart.ChartServlet; @@ -110,12 +112,15 @@ private LegendPosition getLegendPosition() { private final ItemUIRegistry itemUIRegistry; private final PersistenceServiceRegistry persistenceServiceRegistry; + private final PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry; @Activate public DefaultChartProvider(final @Reference ItemUIRegistry itemUIRegistry, - final @Reference PersistenceServiceRegistry persistenceServiceRegistry) { + final @Reference PersistenceServiceRegistry persistenceServiceRegistry, + final @Reference PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry) { this.itemUIRegistry = itemUIRegistry; this.persistenceServiceRegistry = persistenceServiceRegistry; + this.persistenceServiceConfigurationRegistry = persistenceServiceConfigurationRegistry; if (logger.isDebugEnabled()) { logger.debug("Available themes for default chart provider: {}", String.join(", ", CHART_THEMES.keySet())); @@ -188,8 +193,8 @@ public BufferedImage createChart(@Nullable String serviceId, @Nullable String th // axis styler.setAxisTickLabelsFont(chartTheme.getAxisTickLabelsFont(dpi)); styler.setAxisTickLabelsColor(chartTheme.getAxisTickLabelsColor()); - styler.setXAxisMin((double) startTime.toInstant().toEpochMilli()); - styler.setXAxisMax((double) endTime.toInstant().toEpochMilli()); + styler.setXAxisMin(startTime.toInstant().toEpochMilli()); + styler.setXAxisMax(endTime.toInstant().toEpochMilli()); int yAxisSpacing = Math.max(height / 10, chartTheme.getAxisTickLabelsFont(dpi).getSize()); if (yAxisDecimalPattern != null) { styler.setYAxisDecimalPattern(yAxisDecimalPattern); @@ -333,10 +338,13 @@ private boolean addItem(XYChart chart, QueryablePersistenceService service, Zone // after the start of the graph (or not at all if there's no change during the graph period) filter = new FilterCriteria(); filter.setEndDate(timeBegin); - filter.setItemName(item.getName()); + String itemName = item.getName(); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(service.getId()); + String alias = config != null ? config.getAliases().get(itemName) : null; + filter.setItemName(itemName); filter.setPageSize(1); filter.setOrdering(Ordering.DESCENDING); - result = service.query(filter); + result = service.query(filter, alias); if (result.iterator().hasNext()) { HistoricItem historicItem = result.iterator().next(); @@ -352,7 +360,7 @@ private boolean addItem(XYChart chart, QueryablePersistenceService service, Zone filter.setOrdering(Ordering.ASCENDING); // Get the data from the persistence store - result = service.query(filter); + result = service.query(filter, alias); // Iterate through the data for (HistoricItem historicItem : result) {