From 6a31f41a02fd143a59f706e211f6d101bcaf707e Mon Sep 17 00:00:00 2001 From: Antoine Bouhours Date: Tue, 24 Sep 2024 11:10:02 +0200 Subject: [PATCH] Lazy loading of extension attributes (#411) Extension attributes are loaded in the identifiable only when needed (i.e: when getExtension(), getExtensionByName(), getExtensions() or removeExtension() are called). The collection cache is enriched to cache extension attributes similarly to identifiable resources. Signed-off-by: Antoine Bouhours --- .../client/PreloadingNetworkStoreClient.java | 12 + .../network/store/client/RestClient.java | 8 + .../network/store/client/RestClientImpl.java | 13 + .../store/client/RestNetworkStoreClient.java | 68 ++ .../client/CachedNetworkStoreClientTest.java | 198 ++++- .../PreloadingNetworkStoreClientTest.java | 163 +++- .../iidm/impl/AbstractIdentifiableImpl.java | 36 +- .../iidm/impl/CachedNetworkStoreClient.java | 325 +++++--- .../store/iidm/impl/CollectionCache.java | 189 ++++- .../store/iidm/impl/NetworkObjectIndex.java | 12 + .../store/iidm/impl/NetworkStoreClient.java | 32 + .../iidm/impl/OfflineNetworkStoreClient.java | 30 +- .../store/iidm/impl/CollectionCacheTest.java | 243 +++++- .../iidm/impl/MockNetworkStoreClient.java | 706 ++++++++++++++++++ .../model/ExtensionAttributesIdResolver.java | 2 +- .../network/store/model/ExtensionLoaders.java | 40 +- .../network/store/model/LoaderTest.java | 16 +- .../store/model/tools/DuplicatedLoader1.java | 4 +- .../store/model/tools/DuplicatedLoader2.java | 4 +- 19 files changed, 1938 insertions(+), 163 deletions(-) create mode 100644 network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/MockNetworkStoreClient.java diff --git a/network-store-client/src/main/java/com/powsybl/network/store/client/PreloadingNetworkStoreClient.java b/network-store-client/src/main/java/com/powsybl/network/store/client/PreloadingNetworkStoreClient.java index 1f26c28d6..3e064a117 100644 --- a/network-store-client/src/main/java/com/powsybl/network/store/client/PreloadingNetworkStoreClient.java +++ b/network-store-client/src/main/java/com/powsybl/network/store/client/PreloadingNetworkStoreClient.java @@ -871,4 +871,16 @@ public void removeConfiguredBuses(UUID networkUuid, int variantNum, List ensureCached(ResourceType.CONFIGURED_BUS, networkUuid, variantNum); delegate.removeConfiguredBuses(networkUuid, variantNum, configuredBusesId); } + + @Override + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + delegate.getAllExtensionsAttributesByResourceTypeAndExtensionName(networkUuid, variantNum, resourceType, extensionName); + return delegate.getExtensionAttributes(networkUuid, variantNum, resourceType, identifiableId, extensionName); + } + + @Override + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String id) { + delegate.getAllExtensionsAttributesByResourceType(networkUuid, variantNum, resourceType); + return delegate.getAllExtensionsAttributesByIdentifiableId(networkUuid, variantNum, resourceType, id); + } } diff --git a/network-store-client/src/main/java/com/powsybl/network/store/client/RestClient.java b/network-store-client/src/main/java/com/powsybl/network/store/client/RestClient.java index 491ebe675..6982718d4 100644 --- a/network-store-client/src/main/java/com/powsybl/network/store/client/RestClient.java +++ b/network-store-client/src/main/java/com/powsybl/network/store/client/RestClient.java @@ -7,6 +7,7 @@ package com.powsybl.network.store.client; import com.powsybl.network.store.model.Attributes; +import com.powsybl.network.store.model.ExtensionAttributes; import com.powsybl.network.store.model.IdentifiableAttributes; import com.powsybl.network.store.model.Resource; import org.springframework.core.ParameterizedTypeReference; @@ -23,6 +24,13 @@ public interface RestClient { Optional> getOne(String target, String url, Object... uriVariables); + /** + * Retrieves one extension attributes from the server. + * @return {@link ExtensionAttributes} which is a subset of an identifiable resource. The extension attributes can be put in the extensionAttributes + * map of an {@link IdentifiableAttributes} or used to load an extension. + */ + Optional getOneExtensionAttributes(String url, Object... uriVariables); + List> getAll(String target, String url, Object... uriVariables); void updateAll(String url, List> resources, Object... uriVariables); diff --git a/network-store-client/src/main/java/com/powsybl/network/store/client/RestClientImpl.java b/network-store-client/src/main/java/com/powsybl/network/store/client/RestClientImpl.java index ac2128775..28c6fd2d2 100644 --- a/network-store-client/src/main/java/com/powsybl/network/store/client/RestClientImpl.java +++ b/network-store-client/src/main/java/com/powsybl/network/store/client/RestClientImpl.java @@ -103,6 +103,19 @@ public Optional> getOne(String ta } } + @Override + public Optional getOneExtensionAttributes(String url, Object... uriVariables) { + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<>() { }, uriVariables); + if (response.getStatusCode() == HttpStatus.OK) { + ExtensionAttributes body = response.getBody(); + return Optional.of(body); + } else if (response.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw createHttpException(url, "get", response.getStatusCode()); + } + } + @Override public List> getAll(String target, String url, Object... uriVariables) { ResponseEntity> response = getDocument(url, uriVariables); diff --git a/network-store-client/src/main/java/com/powsybl/network/store/client/RestNetworkStoreClient.java b/network-store-client/src/main/java/com/powsybl/network/store/client/RestNetworkStoreClient.java index 1fa8ff3fc..a0c68d986 100644 --- a/network-store-client/src/main/java/com/powsybl/network/store/client/RestNetworkStoreClient.java +++ b/network-store-client/src/main/java/com/powsybl/network/store/client/RestNetworkStoreClient.java @@ -115,6 +115,49 @@ private Optional> get(String targ return resource; } + private Optional getExtensionAttributes(String urlTemplate, Object... uriVariables) { + logGetExtensionAttributesUrl(urlTemplate, uriVariables); + Stopwatch stopwatch = Stopwatch.createStarted(); + Optional extensionAttributes = restClient.getOneExtensionAttributes(urlTemplate, uriVariables); + stopwatch.stop(); + logGetExtensionAttributesTime(extensionAttributes.isPresent() ? 1 : 0, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + + return extensionAttributes; + } + + private Map getExtensionAttributesMap(String urlTemplate, Object... uriVariables) { + logGetExtensionAttributesUrl(urlTemplate, uriVariables); + Stopwatch stopwatch = Stopwatch.createStarted(); + Map extensionAttributes = restClient.get(urlTemplate, new ParameterizedTypeReference<>() { }, uriVariables); + stopwatch.stop(); + logGetExtensionAttributesTime(extensionAttributes.size(), stopwatch.elapsed(TimeUnit.MILLISECONDS)); + + return extensionAttributes; + } + + private Map> getExtensionAttributesNestedMap(String urlTemplate, Object... uriVariables) { + logGetExtensionAttributesUrl(urlTemplate, uriVariables); + Stopwatch stopwatch = Stopwatch.createStarted(); + Map> extensionAttributes = restClient.get(urlTemplate, new ParameterizedTypeReference<>() { }, uriVariables); + stopwatch.stop(); + long loadedAttributesCount = extensionAttributes.values().stream() + .mapToLong(innerMap -> innerMap.values().size()) + .sum(); + logGetExtensionAttributesTime(loadedAttributesCount, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + + return extensionAttributes; + } + + private static void logGetExtensionAttributesUrl(String urlTemplate, Object... uriVariables) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Loading extension attributes {}", UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables)); + } + } + + private static void logGetExtensionAttributesTime(long loadedAttributesCount, long timeElapsed) { + LOGGER.info("{} extension attributes loaded in {} ms", loadedAttributesCount, timeElapsed); + } + private void updatePartition(String target, String url, AttributeFilter attributeFilter, List> resources, Object[] uriVariables) { if (attributeFilter == null) { if (LOGGER.isInfoEnabled()) { @@ -848,4 +891,29 @@ public void updateGrounds(UUID networkUuid, List> gro AttributeFilter attributeFilter) { updateAll(STR_GROUND, "/networks/{networkUuid}/grounds", groundResources, attributeFilter, networkUuid); } + + @Override + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + return getExtensionAttributes("/networks/{networkUuid}/{variantNum}/identifiables/{identifiableId}/extensions/{extensionName}", networkUuid, variantNum, identifiableId, extensionName); + } + + @Override + public Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID networkUuid, int variantNum, ResourceType resourceType, String extensionName) { + return getExtensionAttributesMap("/networks/{networkUuid}/{variantNum}/types/{type}/extensions/{extensionName}", networkUuid, variantNum, resourceType, extensionName); + } + + @Override + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId) { + return getExtensionAttributesMap("/networks/{networkUuid}/{variantNum}/identifiables/{identifiableId}/extensions", networkUuid, variantNum, identifiableId); + } + + @Override + public Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) { + return getExtensionAttributesNestedMap("/networks/{networkUuid}/{variantNum}/types/{resourceType}/extensions", networkUuid, variantNum, resourceType); + } + + @Override + public void removeExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + restClient.delete("/networks/{networkUuid}/{variantNum}/identifiables/{identifiableId}/extensions/{extensionName}", networkUuid, variantNum, identifiableId, extensionName); + } } diff --git a/network-store-client/src/test/java/com/powsybl/network/store/client/CachedNetworkStoreClientTest.java b/network-store-client/src/test/java/com/powsybl/network/store/client/CachedNetworkStoreClientTest.java index af3ff9d84..5975a2af2 100644 --- a/network-store-client/src/test/java/com/powsybl/network/store/client/CachedNetworkStoreClientTest.java +++ b/network-store-client/src/test/java/com/powsybl/network/store/client/CachedNetworkStoreClientTest.java @@ -6,9 +6,15 @@ */ package com.powsybl.network.store.client; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.powsybl.iidm.network.SwitchKind; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.GeneratorStartup; +import com.powsybl.iidm.network.extensions.OperatingStatus; import com.powsybl.network.store.iidm.impl.CachedNetworkStoreClient; import com.powsybl.network.store.model.*; import org.junit.Before; @@ -16,6 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -24,17 +31,18 @@ import java.io.IOException; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.PUT; +import static org.junit.Assert.*; +import static org.springframework.http.HttpMethod.*; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; /** @@ -479,4 +487,186 @@ public void testGetIdentifiable() throws IOException { server.reset(); } } + + @Test + public void testGetExtensionCache() throws IOException { + CachedNetworkStoreClient cachedClient = new CachedNetworkStoreClient(new BufferedNetworkStoreClient(restStoreClient, ForkJoinPool.commonPool())); + UUID networkUuid = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4"); + String identifiableId = "GEN"; + + // Load the identifiable in the cache + loadIdentifiableToCache(identifiableId, networkUuid, cachedClient); + + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + ActivePowerControlAttributes apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + getExtensionAttributes(apc1, networkUuid, identifiableId, cachedClient, ActivePowerControl.NAME); + + // Not found extension + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions/" + ConnectablePosition.NAME)) + .andExpect(method(GET)) + .andRespond(withStatus(HttpStatus.NOT_FOUND)); + + Optional notFoundExtensionAttributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, ConnectablePosition.NAME); + assertFalse(notFoundExtensionAttributes.isPresent()); + server.verify(); + server.reset(); + + removeExtensionAttributes(networkUuid, identifiableId, cachedClient, ActivePowerControl.NAME); + + // When removing the generator, the extension attributes should be removed from the cache as well + GeneratorStartupAttributes gs1 = GeneratorStartupAttributes.builder() + .marginalCost(6.8) + .forcedOutageRate(35) + .plannedOutageRate(30) + .startupCost(28) + .plannedActivePowerSetpoint(5) + .build(); + String oneExtensionAttributes = objectMapper.writeValueAsString(gs1); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions/" + GeneratorStartup.NAME)) + .andExpect(method(GET)) + .andRespond(withSuccess(oneExtensionAttributes, MediaType.APPLICATION_JSON)); + + Optional gs1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, GeneratorStartup.NAME); + assertTrue(gs1Attributes.isPresent()); + + cachedClient.removeGenerators(networkUuid, Resource.INITIAL_VARIANT_NUM, List.of(identifiableId)); + gs1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, GeneratorStartup.NAME); + assertFalse(gs1Attributes.isPresent()); + } + + private void removeExtensionAttributes(UUID networkUuid, String identifiableId, CachedNetworkStoreClient cachedClient, String extensionName) { + // When calling removeExtensionAttributes, the attributes should be removed from the cache and no new request should be done + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions/" + extensionName)) + .andExpect(method(DELETE)) + .andRespond(withSuccess()); + cachedClient.removeExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, extensionName); + + Optional extensionAttributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, extensionName); + assertFalse(extensionAttributes.isPresent()); + server.verify(); + server.reset(); + } + + private void getExtensionAttributes(ExtensionAttributes extensionAttributes, UUID networkUuid, String identifiableId, CachedNetworkStoreClient cachedClient, String extensionName) throws JsonProcessingException { + String oneExtensionAttributes = objectMapper.writeValueAsString(extensionAttributes); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions/" + extensionName)) + .andExpect(method(GET)) + .andRespond(withSuccess(oneExtensionAttributes, MediaType.APPLICATION_JSON)); + + Optional extensionAttributesResult = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, extensionName); + assertTrue(extensionAttributesResult.isPresent()); + + extensionAttributesResult = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, extensionName); + assertTrue(extensionAttributesResult.isPresent()); + + server.verify(); + server.reset(); + } + + @Test + public void testGetExtensionsCache() throws IOException { + CachedNetworkStoreClient cachedClient = new CachedNetworkStoreClient(new BufferedNetworkStoreClient(restStoreClient, ForkJoinPool.commonPool())); + UUID networkUuid = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4"); + String identifiableId = "GEN"; + + // Load the identifiable in the cache + loadIdentifiableToCache(identifiableId, networkUuid, cachedClient); + + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + ActivePowerControlAttributes apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + + GeneratorStartupAttributes gs1 = GeneratorStartupAttributes.builder() + .marginalCost(6.8) + .forcedOutageRate(35) + .plannedOutageRate(30) + .startupCost(28) + .plannedActivePowerSetpoint(5) + .build(); + + String multipleExtensionAttributes = objectMapper.writerFor(new TypeReference>() { + }).writeValueAsString(Map.of(ActivePowerControl.NAME, apc1, GeneratorStartup.NAME, gs1)); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions")) + .andExpect(method(GET)) + .andRespond(withSuccess(multipleExtensionAttributes, MediaType.APPLICATION_JSON)); + + Map extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId); + assertEquals(2, extensionAttributesMap.size()); + + extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId); + assertEquals(2, extensionAttributesMap.size()); + + server.verify(); + server.reset(); + + // When calling removeExtensionAttributes, the attributes should be removed from the cache and no new request should be done + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/identifiables/" + identifiableId + "/extensions/" + ActivePowerControl.NAME)) + .andExpect(method(DELETE)) + .andRespond(withSuccess()); + cachedClient.removeExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, ActivePowerControl.NAME); + + extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId); + assertEquals(1, extensionAttributesMap.size()); + assertNull(extensionAttributesMap.get(ActivePowerControl.NAME)); + server.verify(); + server.reset(); + } + + @Test + public void testGetExtensionCacheWithClonedNetwork() throws IOException { + CachedNetworkStoreClient cachedClient = new CachedNetworkStoreClient(new BufferedNetworkStoreClient(restStoreClient, ForkJoinPool.commonPool())); + UUID networkUuid = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4"); + int targetVariantNum = 1; + String targetVariantId = "new_variant"; + String identifiableId = "GEN"; + + loadIdentifiableToCache(identifiableId, networkUuid, cachedClient); + ActivePowerControlAttributes apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + getExtensionAttributes(apc1, networkUuid, identifiableId, cachedClient, ActivePowerControl.NAME); + OperatingStatusAttributes os1 = OperatingStatusAttributes.builder() + .operatingStatus("foo") + .build(); + getExtensionAttributes(os1, networkUuid, identifiableId, cachedClient, OperatingStatus.NAME); + + // Remove extension attributes to check that the removed cache is cloned + removeExtensionAttributes(networkUuid, identifiableId, cachedClient, OperatingStatus.NAME); + // When cloning the network, the cached attributes should remained cached + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/to/" + targetVariantNum + "?targetVariantId=" + targetVariantId)) + .andExpect(method(PUT)) + .andRespond(withSuccess()); + cachedClient.cloneNetwork(networkUuid, Resource.INITIAL_VARIANT_NUM, targetVariantNum, targetVariantId); + Optional apc1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, ActivePowerControl.NAME); + assertTrue(apc1Attributes.isPresent()); + Optional os1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId, OperatingStatus.NAME); + assertFalse(os1Attributes.isPresent()); + server.verify(); + server.reset(); + } + + private void loadIdentifiableToCache(String identifiableId, UUID networkUuid, CachedNetworkStoreClient cachedClient) throws JsonProcessingException { + Resource g1Resource = Resource.generatorBuilder() + .id(identifiableId) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("VL_1") + .build()) + .build(); + String generatorJson = objectMapper.writeValueAsString(TopLevelDocument.of(List.of(g1Resource))); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/generators/" + identifiableId)) + .andExpect(method(GET)) + .andRespond(withSuccess(generatorJson, MediaType.APPLICATION_JSON)); + cachedClient.getGenerator(networkUuid, Resource.INITIAL_VARIANT_NUM, identifiableId); + server.verify(); + server.reset(); + } } diff --git a/network-store-client/src/test/java/com/powsybl/network/store/client/PreloadingNetworkStoreClientTest.java b/network-store-client/src/test/java/com/powsybl/network/store/client/PreloadingNetworkStoreClientTest.java index 745d6858e..f0c981ec4 100644 --- a/network-store-client/src/test/java/com/powsybl/network/store/client/PreloadingNetworkStoreClientTest.java +++ b/network-store-client/src/test/java/com/powsybl/network/store/client/PreloadingNetworkStoreClientTest.java @@ -6,11 +6,15 @@ */ package com.powsybl.network.store.client; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.powsybl.iidm.network.Country; import com.powsybl.iidm.network.LoadType; import com.powsybl.iidm.network.SwitchKind; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.GeneratorStartup; import com.powsybl.network.store.iidm.impl.CachedNetworkStoreClient; import com.powsybl.network.store.model.*; import org.junit.Before; @@ -25,13 +29,10 @@ import org.springframework.test.web.client.MockRestServiceServer; import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ForkJoinPool; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import static org.springframework.http.HttpMethod.GET; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; @@ -807,4 +808,156 @@ public void testConfiguredBusCache() throws IOException { assertEquals(0, cachedClient.getConfiguredBuses(networkUuid, Resource.INITIAL_VARIANT_NUM).size()); server.verify(); } + + @Test + public void testGetExtensionCache() throws IOException { + String identifiableId1 = "GEN"; + String identifiableId2 = "GEN1"; + + // Load the identifiables in the cache + loadTwoIdentifiablesToCache(identifiableId1, identifiableId2); + + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + ActivePowerControlAttributes apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + ActivePowerControlAttributes apc2 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(1) + .build(); + + String extensionAttributes = objectMapper.writerFor(new TypeReference>() { + }).writeValueAsString(Map.of(identifiableId1, apc1, identifiableId2, apc2)); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/types/" + ResourceType.GENERATOR + "/extensions/activepowercontrol")) + .andExpect(method(GET)) + .andRespond(withSuccess(extensionAttributes, MediaType.APPLICATION_JSON)); + + Optional apc1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1, "activepowercontrol"); + assertTrue(apc1Attributes.isPresent()); + ActivePowerControlAttributes activePowerControlAttributes = (ActivePowerControlAttributes) apc1Attributes.get(); + assertEquals(0.5, activePowerControlAttributes.getParticipationFactor(), 0); + + apc1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1, "activepowercontrol"); + assertTrue(apc1Attributes.isPresent()); + activePowerControlAttributes = (ActivePowerControlAttributes) apc1Attributes.get(); + assertEquals(0.5, activePowerControlAttributes.getParticipationFactor(), 0); + + server.verify(); + server.reset(); + } + + @Test + public void testGetExtensionEmptyExtensionAttributesCache() throws IOException { + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + String identifiableId1 = "GEN"; + String extensionAttributes = objectMapper.writerFor(new TypeReference>() { + }).writeValueAsString(Map.of()); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/types/" + ResourceType.GENERATOR + "/extensions/activepowercontrol")) + .andExpect(method(GET)) + .andRespond(withSuccess(extensionAttributes, MediaType.APPLICATION_JSON)); + + Optional apc1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1, "activepowercontrol"); + assertFalse(apc1Attributes.isPresent()); + + apc1Attributes = cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1, "activepowercontrol"); + assertFalse(apc1Attributes.isPresent()); + + server.verify(); + server.reset(); + } + + @Test + public void testGetExtensionsCache() throws IOException { + String identifiableId1 = "GEN"; + String identifiableId2 = "GEN1"; + + // Load the identifiables in the cache + loadTwoIdentifiablesToCache(identifiableId1, identifiableId2); + + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + ActivePowerControlAttributes apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + GeneratorStartupAttributes gs1 = GeneratorStartupAttributes.builder() + .marginalCost(6.8) + .forcedOutageRate(35) + .plannedOutageRate(30) + .startupCost(28) + .plannedActivePowerSetpoint(5) + .build(); + ActivePowerControlAttributes apc2 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(1) + .build(); + + String multipleExtensionAttributes = objectMapper.writerFor(new TypeReference>>() { + }).writeValueAsString(Map.of(identifiableId1, Map.of(ActivePowerControl.NAME, apc1, GeneratorStartup.NAME, gs1), identifiableId2, Map.of(ActivePowerControl.NAME, apc2))); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/types/" + ResourceType.GENERATOR + "/extensions")) + .andExpect(method(GET)) + .andRespond(withSuccess(multipleExtensionAttributes, MediaType.APPLICATION_JSON)); + + Map extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1); + assertEquals(2, extensionAttributesMap.size()); + + extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1); + assertEquals(2, extensionAttributesMap.size()); + + // Check that there is no new fetch when getting a single extension once all the extensions have been loaded in the identifiable + cachedClient.getExtensionAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1, "activepowercontrol"); + + server.verify(); + server.reset(); + } + + @Test + public void testGetExtensionsEmptyExtensionAttributesCache() throws IOException { + String identifiableId1 = "GEN"; + String identifiableId2 = "GEN1"; + + loadTwoIdentifiablesToCache(identifiableId1, identifiableId2); + + // Two successive ExtensionAttributes retrieval, only the first should send a REST request, the second uses the cache + String multipleExtensionAttributes = objectMapper.writerFor(new TypeReference>>() { + }).writeValueAsString(Map.of()); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/types/" + ResourceType.GENERATOR + "/extensions")) + .andExpect(method(GET)) + .andRespond(withSuccess(multipleExtensionAttributes, MediaType.APPLICATION_JSON)); + + Map extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1); + assertEquals(0, extensionAttributesMap.size()); + + extensionAttributesMap = cachedClient.getAllExtensionsAttributesByIdentifiableId(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.GENERATOR, identifiableId1); + assertEquals(0, extensionAttributesMap.size()); + + server.verify(); + server.reset(); + } + + private void loadTwoIdentifiablesToCache(String identifiableId1, String identifiableId2) throws JsonProcessingException { + Resource g1Resource = Resource.generatorBuilder() + .id(identifiableId1) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("VL_1") + .build()) + .build(); + Resource g2Resource = Resource.generatorBuilder() + .id(identifiableId2) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("VL_1") + .build()) + .build(); + String generatorJson = objectMapper.writeValueAsString(TopLevelDocument.of(List.of(g1Resource, g2Resource))); + server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/generators")) + .andExpect(method(GET)) + .andRespond(withSuccess(generatorJson, MediaType.APPLICATION_JSON)); + cachedClient.getGenerator(networkUuid, Resource.INITIAL_VARIANT_NUM, identifiableId1); + server.verify(); + server.reset(); + } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractIdentifiableImpl.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractIdentifiableImpl.java index 4e9df2107..b8fa8da17 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractIdentifiableImpl.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractIdentifiableImpl.java @@ -24,10 +24,11 @@ import org.apache.commons.lang3.mutable.MutableObject; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.stream.Collectors; +import static com.powsybl.network.store.model.ExtensionLoaders.loaderExists; + /** * @author Geoffroy Jamgotchian */ @@ -298,26 +299,19 @@ public > void addExtension(Class type, E exten } public > E getExtension(Class type) { - List matchingExtensions = resource.getAttributes().getExtensionAttributes().keySet().stream() - .map(ExtensionLoaders::findLoader) - .filter(loader -> type == loader.getType()) - .map(loader -> (E) loader.load(this)) - .collect(Collectors.toList()); - - if (matchingExtensions.isEmpty()) { + if (!loaderExists(type)) { return null; } - - if (matchingExtensions.size() > 1) { - throw new PowsyblException("More than one extension found for type: " + type.getSimpleName()); - } - - return matchingExtensions.get(0); + return getExtensionByName(ExtensionLoaders.findLoader(type).getName()); } public > E getExtensionByName(String name) { + if (!loaderExists(name)) { + return null; + } + index.loadExtensionAttributes(resource.getType(), resource.getId(), name); if (resource.getAttributes().getExtensionAttributes().containsKey(name)) { - return (E) ExtensionLoaders.findLoader(name).load(this); + return (E) ExtensionLoaders.findLoaderByName(name).load(this); } return null; } @@ -327,18 +321,16 @@ public > boolean removeExtension(Class type) { if (extension == null) { return false; } - AtomicBoolean removed = new AtomicBoolean(false); index.notifyExtensionBeforeRemoval(extension); - updateResource(r -> removed.set(r.getAttributes().getExtensionAttributes().remove(extension.getName()) != null)); - if (removed.get()) { - index.notifyExtensionAfterRemoval(this, extension.getName()); - } - return removed.get(); + index.removeExtensionAttributes(resource.getType(), resource.getId(), extension.getName()); + index.notifyExtensionAfterRemoval(this, extension.getName()); + return true; } public > Collection getExtensions() { + index.loadAllExtensionsAttributesByIdentifiableId(resource.getType(), resource.getId()); return resource.getAttributes().getExtensionAttributes().keySet().stream() - .map(name -> (E) ExtensionLoaders.findLoader(name).load(this)) + .map(name -> (E) ExtensionLoaders.findLoaderByName(name).load(this)) .collect(Collectors.toList()); } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CachedNetworkStoreClient.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CachedNetworkStoreClient.java index cdc871761..0db15691b 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CachedNetworkStoreClient.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CachedNetworkStoreClient.java @@ -29,105 +29,165 @@ public class CachedNetworkStoreClient extends AbstractForwardingNetworkStoreClie private final Map> variantsInfosByNetworkUuid = new HashMap<>(); - private final NetworkCollectionIndex> networksCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - (networkUuid, variantNum, id) -> delegate.getNetwork(networkUuid, variantNum), - null, - (networkUuid, variantNum) -> delegate.getNetwork(networkUuid, variantNum).stream().collect(Collectors.toList()))); - - private final NetworkCollectionIndex> substationsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getSubstation, - null, - delegate::getSubstations)); - - private final NetworkCollectionIndex> voltageLevelsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getVoltageLevel, - delegate::getVoltageLevelsInSubstation, - delegate::getVoltageLevels)); - - private final NetworkCollectionIndex> switchesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getSwitch, - delegate::getVoltageLevelSwitches, - delegate::getSwitches)); - - private final NetworkCollectionIndex> busbarSectionsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getBusbarSection, - delegate::getVoltageLevelBusbarSections, - delegate::getBusbarSections)); - - private final NetworkCollectionIndex> loadsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getLoad, - delegate::getVoltageLevelLoads, - delegate::getLoads)); - - private final NetworkCollectionIndex> generatorsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getGenerator, - delegate::getVoltageLevelGenerators, - delegate::getGenerators)); - - private final NetworkCollectionIndex> batteriesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getBattery, - delegate::getVoltageLevelBatteries, - delegate::getBatteries)); - - private final NetworkCollectionIndex> twoWindingsTransformerCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getTwoWindingsTransformer, - delegate::getVoltageLevelTwoWindingsTransformers, - delegate::getTwoWindingsTransformers)); - - private final NetworkCollectionIndex> threeWindingsTranqformerCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getThreeWindingsTransformer, - delegate::getVoltageLevelThreeWindingsTransformers, - delegate::getThreeWindingsTransformers)); - - private final NetworkCollectionIndex> linesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getLine, - delegate::getVoltageLevelLines, - delegate::getLines)); - - private final NetworkCollectionIndex> shuntCompensatorsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getShuntCompensator, - delegate::getVoltageLevelShuntCompensators, - delegate::getShuntCompensators)); - - private final NetworkCollectionIndex> vscConverterStationCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getVscConverterStation, - delegate::getVoltageLevelVscConverterStations, - delegate::getVscConverterStations)); - - private final NetworkCollectionIndex> lccConverterStationCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getLccConverterStation, - delegate::getVoltageLevelLccConverterStations, - delegate::getLccConverterStations)); - - private final NetworkCollectionIndex> staticVarCompensatorCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getStaticVarCompensator, - delegate::getVoltageLevelStaticVarCompensators, - delegate::getStaticVarCompensators)); - - private final NetworkCollectionIndex> hvdcLinesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getHvdcLine, - null, - delegate::getHvdcLines)); - - private final NetworkCollectionIndex> danglingLinesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getDanglingLine, - delegate::getVoltageLevelDanglingLines, - delegate::getDanglingLines)); - - private final NetworkCollectionIndex> configuredBusesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getConfiguredBus, - delegate::getVoltageLevelConfiguredBuses, - delegate::getConfiguredBuses)); - - private final NetworkCollectionIndex> tieLinesCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getTieLine, - null, - delegate::getTieLines)); - - private final NetworkCollectionIndex> groundsCache = new NetworkCollectionIndex<>(() -> new CollectionCache<>( - delegate::getGround, - delegate::getVoltageLevelGrounds, - delegate::getGrounds)); + private final NetworkCollectionIndex> networksCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + (networkUuid, variantNum, id) -> delegate.getNetwork(networkUuid, variantNum), + null, + (networkUuid, variantNum) -> delegate.getNetwork(networkUuid, variantNum).stream().collect(Collectors.toList()), + delegate) + ); + + private final NetworkCollectionIndex> substationsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getSubstation, + null, + delegate::getSubstations, + delegate) + ); + + private final NetworkCollectionIndex> voltageLevelsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getVoltageLevel, + delegate::getVoltageLevelsInSubstation, + delegate::getVoltageLevels, + delegate) + ); + + private final NetworkCollectionIndex> switchesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getSwitch, + delegate::getVoltageLevelSwitches, + delegate::getSwitches, + delegate) + ); + + private final NetworkCollectionIndex> busbarSectionsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getBusbarSection, + delegate::getVoltageLevelBusbarSections, + delegate::getBusbarSections, + delegate) + ); + + private final NetworkCollectionIndex> loadsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getLoad, + delegate::getVoltageLevelLoads, + delegate::getLoads, + delegate) + ); + + private final NetworkCollectionIndex> generatorsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getGenerator, + delegate::getVoltageLevelGenerators, + delegate::getGenerators, + delegate) + ); + + private final NetworkCollectionIndex> batteriesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getBattery, + delegate::getVoltageLevelBatteries, + delegate::getBatteries, + delegate) + ); + + private final NetworkCollectionIndex> twoWindingsTransformerCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getTwoWindingsTransformer, + delegate::getVoltageLevelTwoWindingsTransformers, + delegate::getTwoWindingsTransformers, + delegate) + ); + + private final NetworkCollectionIndex> threeWindingsTransformerCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getThreeWindingsTransformer, + delegate::getVoltageLevelThreeWindingsTransformers, + delegate::getThreeWindingsTransformers, + delegate) + ); + + private final NetworkCollectionIndex> linesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getLine, + delegate::getVoltageLevelLines, + delegate::getLines, + delegate) + ); + + private final NetworkCollectionIndex> shuntCompensatorsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getShuntCompensator, + delegate::getVoltageLevelShuntCompensators, + delegate::getShuntCompensators, + delegate) + ); + + private final NetworkCollectionIndex> vscConverterStationCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getVscConverterStation, + delegate::getVoltageLevelVscConverterStations, + delegate::getVscConverterStations, + delegate) + ); + + private final NetworkCollectionIndex> lccConverterStationCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getLccConverterStation, + delegate::getVoltageLevelLccConverterStations, + delegate::getLccConverterStations, + delegate) + ); + + private final NetworkCollectionIndex> staticVarCompensatorCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getStaticVarCompensator, + delegate::getVoltageLevelStaticVarCompensators, + delegate::getStaticVarCompensators, + delegate) + ); + + private final NetworkCollectionIndex> hvdcLinesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getHvdcLine, + null, + delegate::getHvdcLines, + delegate) + ); + + private final NetworkCollectionIndex> danglingLinesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getDanglingLine, + delegate::getVoltageLevelDanglingLines, + delegate::getDanglingLines, + delegate) + ); + + private final NetworkCollectionIndex> configuredBusesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getConfiguredBus, + delegate::getVoltageLevelConfiguredBuses, + delegate::getConfiguredBuses, + delegate) + ); + + private final NetworkCollectionIndex> tieLinesCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getTieLine, + null, + delegate::getTieLines, + delegate) + ); + + private final NetworkCollectionIndex> groundsCache = + new NetworkCollectionIndex<>(() -> new CollectionCache<>( + delegate::getGround, + delegate::getVoltageLevelGrounds, + delegate::getGrounds, + delegate) + ); private final Map>> voltageLevelContainersCaches = new EnumMap<>(ResourceType.class); @@ -145,7 +205,7 @@ public CachedNetworkStoreClient(NetworkStoreClient delegate) { voltageLevelContainersCaches.put(ResourceType.GENERATOR, generatorsCache); voltageLevelContainersCaches.put(ResourceType.BATTERY, batteriesCache); voltageLevelContainersCaches.put(ResourceType.TWO_WINDINGS_TRANSFORMER, twoWindingsTransformerCache); - voltageLevelContainersCaches.put(ResourceType.THREE_WINDINGS_TRANSFORMER, threeWindingsTranqformerCache); + voltageLevelContainersCaches.put(ResourceType.THREE_WINDINGS_TRANSFORMER, threeWindingsTransformerCache); voltageLevelContainersCaches.put(ResourceType.LINE, linesCache); voltageLevelContainersCaches.put(ResourceType.SHUNT_COMPENSATOR, shuntCompensatorsCache); voltageLevelContainersCaches.put(ResourceType.VSC_CONVERTER_STATION, vscConverterStationCache); @@ -254,7 +314,7 @@ public void cloneNetwork(UUID networkUuid, int sourceVariantNum, int targetVaria cloneCollection(generatorsCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); cloneCollection(batteriesCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); cloneCollection(twoWindingsTransformerCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); - cloneCollection(threeWindingsTranqformerCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); + cloneCollection(threeWindingsTransformerCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); cloneCollection(linesCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); cloneCollection(shuntCompensatorsCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); cloneCollection(vscConverterStationCache, networkUuid, sourceVariantNum, targetVariantNum, objectMapper); @@ -456,13 +516,13 @@ public void removeTwoWindingsTransformers(UUID networkUuid, int variantNum, List @Override public List> getVoltageLevelThreeWindingsTransformers(UUID networkUuid, int variantNum, String voltageLevelId) { - return threeWindingsTranqformerCache.getCollection(networkUuid, variantNum).getContainerResources(networkUuid, variantNum, voltageLevelId); + return threeWindingsTransformerCache.getCollection(networkUuid, variantNum).getContainerResources(networkUuid, variantNum, voltageLevelId); } @Override public void removeThreeWindingsTransformers(UUID networkUuid, int variantNum, List threeWindingsTransformersId) { delegate.removeThreeWindingsTransformers(networkUuid, variantNum, threeWindingsTransformersId); - threeWindingsTranqformerCache.getCollection(networkUuid, variantNum).removeResources(threeWindingsTransformersId); + threeWindingsTransformerCache.getCollection(networkUuid, variantNum).removeResources(threeWindingsTransformersId); removeIdentifiableIds(networkUuid, variantNum, threeWindingsTransformersId); } @@ -680,26 +740,26 @@ public void updateTwoWindingsTransformers(UUID networkUuid, List> threeWindingsTransformerResources) { delegate.createThreeWindingsTransformers(networkUuid, threeWindingsTransformerResources); for (Resource threeWindingsTransformerResource : threeWindingsTransformerResources) { - threeWindingsTranqformerCache.getCollection(networkUuid, threeWindingsTransformerResource.getVariantNum()).createResource(threeWindingsTransformerResource); + threeWindingsTransformerCache.getCollection(networkUuid, threeWindingsTransformerResource.getVariantNum()).createResource(threeWindingsTransformerResource); addIdentifiableId(networkUuid, threeWindingsTransformerResource); } } @Override public List> getThreeWindingsTransformers(UUID networkUuid, int variantNum) { - return threeWindingsTranqformerCache.getCollection(networkUuid, variantNum).getResources(networkUuid, variantNum); + return threeWindingsTransformerCache.getCollection(networkUuid, variantNum).getResources(networkUuid, variantNum); } @Override public Optional> getThreeWindingsTransformer(UUID networkUuid, int variantNum, String threeWindingsTransformerId) { - return threeWindingsTranqformerCache.getCollection(networkUuid, variantNum).getResource(networkUuid, variantNum, threeWindingsTransformerId); + return threeWindingsTransformerCache.getCollection(networkUuid, variantNum).getResource(networkUuid, variantNum, threeWindingsTransformerId); } @Override public void updateThreeWindingsTransformers(UUID networkUuid, List> threeWindingsTransformerResources, AttributeFilter attributeFilter) { delegate.updateThreeWindingsTransformers(networkUuid, threeWindingsTransformerResources, attributeFilter); for (Resource threeWindingsTransformerResource : threeWindingsTransformerResources) { - threeWindingsTranqformerCache.getCollection(networkUuid, threeWindingsTransformerResource.getVariantNum()).updateResource(threeWindingsTransformerResource); + threeWindingsTransformerCache.getCollection(networkUuid, threeWindingsTransformerResource.getVariantNum()).updateResource(threeWindingsTransformerResource); } } @@ -1011,6 +1071,57 @@ public void removeConfiguredBuses(UUID networkUuid, int variantNum, List removeIdentifiableIds(networkUuid, variantNum, busesId); } + @Override + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + return getCache(resourceType).getCollection(networkUuid, variantNum).getExtensionAttributes(networkUuid, variantNum, resourceType, identifiableId, extensionName); + } + + @Override + public Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID networkUuid, int variantNum, ResourceType resourceType, String extensionName) { + return getCache(resourceType).getCollection(networkUuid, variantNum).getAllExtensionsAttributesByResourceTypeAndExtensionName(networkUuid, variantNum, resourceType, extensionName); + } + + @Override + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId) { + return getCache(resourceType).getCollection(networkUuid, variantNum).getAllExtensionsAttributesByIdentifiableId(networkUuid, variantNum, resourceType, identifiableId); + } + + @Override + public Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) { + return getCache(resourceType).getCollection(networkUuid, variantNum).getAllExtensionsAttributesByResourceType(networkUuid, variantNum, resourceType); + } + + @Override + public void removeExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + getCache(resourceType).getCollection(networkUuid, variantNum).removeExtensionAttributesByExtensionName(identifiableId, extensionName); + delegate.removeExtensionAttributes(networkUuid, variantNum, resourceType, identifiableId, extensionName); + } + + private NetworkCollectionIndex> getCache(ResourceType resourceType) { + return switch (resourceType) { + case NETWORK -> networksCache; + case SUBSTATION -> substationsCache; + case VOLTAGE_LEVEL -> voltageLevelsCache; + case SWITCH -> switchesCache; + case BUSBAR_SECTION -> busbarSectionsCache; + case LOAD -> loadsCache; + case GENERATOR -> generatorsCache; + case BATTERY -> batteriesCache; + case TWO_WINDINGS_TRANSFORMER -> twoWindingsTransformerCache; + case THREE_WINDINGS_TRANSFORMER -> threeWindingsTransformerCache; + case LINE -> linesCache; + case SHUNT_COMPENSATOR -> shuntCompensatorsCache; + case VSC_CONVERTER_STATION -> vscConverterStationCache; + case LCC_CONVERTER_STATION -> lccConverterStationCache; + case STATIC_VAR_COMPENSATOR -> staticVarCompensatorCache; + case HVDC_LINE -> hvdcLinesCache; + case DANGLING_LINE -> danglingLinesCache; + case CONFIGURED_BUS -> configuredBusesCache; + case TIE_LINE -> tieLinesCache; + case GROUND -> groundsCache; + }; + } + private void addIdentifiableId(UUID networkUuid, Resource resource) { var p = Pair.of(networkUuid, resource.getVariantNum()); Set identifiableIds = identifiablesIdsByNetworkVariant.get(p); diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CollectionCache.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CollectionCache.java index 894a2a1d5..b45734572 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CollectionCache.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CollectionCache.java @@ -48,6 +48,29 @@ public class CollectionCache { */ private final Set removedResources = new HashSet<>(); + /** + * Indicates if the extension has been fully loaded and synchronized with the server. + */ + private final Set fullyLoadedExtensionsByExtensionName = new HashSet<>(); + + /** + * Indicates if all the extensions for a specific identifiable has been fully loaded and synchronized with the server. + */ + private final Set fullyLoadedExtensionsByIdentifiableIds = new HashSet<>(); + + /** + * Indicates if all the extensions for this collection have been fully loaded and synchronized with the server. + */ + private boolean fullyLoadedExtensions = false; + + /** + * Map storing sets of removed extension names associated with identifiable IDs. + * The map is organized where: + * - The keys are identifiable IDs. + * - The values are sets of extension names that have been removed. + */ + private final Map> removedExtensionAttributes = new HashMap<>(); + /** * A function to load one resource from the server. An optional is returned because resource could not exist on * the server. @@ -64,12 +87,15 @@ public class CollectionCache { */ private final BiFunction>> allLoaderFunction; + private final NetworkStoreClient delegate; + public CollectionCache(TriFunction>> oneLoaderFunction, TriFunction>> containerLoaderFunction, - BiFunction>> allLoaderFunction) { + BiFunction>> allLoaderFunction, NetworkStoreClient delegate) { this.oneLoaderFunction = Objects.requireNonNull(oneLoaderFunction); this.containerLoaderFunction = containerLoaderFunction; this.allLoaderFunction = Objects.requireNonNull(allLoaderFunction); + this.delegate = delegate; } public boolean isResourceLoaded(String id) { @@ -240,7 +266,8 @@ public void updateResource(Resource resource) { */ public void removeResource(String id) { Objects.requireNonNull(id); - + // keep track of removed extension attributes + removeExtensionAttributesByIdentifiableId(id); // try to remove the resource from full cache Resource resource = resources.remove(id); removedResources.add(id); @@ -283,7 +310,7 @@ public CollectionCache clone(ObjectMapper objectMapper, int newVariantNum, Co // use json serialization to clone the resources of source collection List> clonedResources = Resource.cloneResourcesToVariant(resources.values(), newVariantNum, objectMapper, resourcePostProcessor); - var clonedCache = new CollectionCache<>(oneLoaderFunction, containerLoaderFunction, allLoaderFunction); + var clonedCache = new CollectionCache<>(oneLoaderFunction, containerLoaderFunction, allLoaderFunction, delegate); for (Resource clonedResource : clonedResources) { clonedCache.resources.put(clonedResource.getId(), clonedResource); } @@ -296,9 +323,165 @@ public CollectionCache clone(ObjectMapper objectMapper, int newVariantNum, Co containerClonedResources.put(id, clonedCache.resources.get(id)); } } + for (Map.Entry> entry : removedExtensionAttributes.entrySet()) { + clonedCache.removedExtensionAttributes.put(entry.getKey(), new HashSet<>(entry.getValue())); + } + + clonedCache.fullyLoadedExtensionsByExtensionName.addAll(fullyLoadedExtensionsByExtensionName); + clonedCache.fullyLoadedExtensionsByIdentifiableIds.addAll(fullyLoadedExtensionsByIdentifiableIds); clonedCache.fullyLoaded = fullyLoaded; + clonedCache.fullyLoadedExtensions = fullyLoadedExtensions; clonedCache.containerFullyLoaded.addAll(containerFullyLoaded); clonedCache.removedResources.addAll(removedResources); return clonedCache; } + + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType type, String identifiableId, String extensionName) { + Objects.requireNonNull(identifiableId); + + if (isExtensionAttributesCached(identifiableId, extensionName)) { + return Optional.ofNullable(getCachedExtensionAttributes(identifiableId).get(extensionName)); + } + + if (!isFullyLoadedExtension(identifiableId, extensionName) && !isRemovedAttributes(identifiableId, extensionName)) { + return delegate.getExtensionAttributes(networkUuid, variantNum, type, identifiableId, extensionName) + .map(attributes -> { + addExtensionAttributesToCache(identifiableId, extensionName, attributes); + return attributes; + }); + } + return Optional.empty(); + } + + private Map getCachedExtensionAttributes(String identifiableId) { + Resource resource = resources.get(identifiableId); + if (resource != null) { + return resource.getAttributes().getExtensionAttributes(); + } else { + throw new PowsyblException("Cannot manipulate extensions for identifiable (" + identifiableId + ") as it has not been loaded into the cache."); + } + } + + private boolean isFullyLoadedExtension(String identifiableId, String extensionName) { + return fullyLoadedExtensions || fullyLoadedExtensionsByIdentifiableIds.contains(identifiableId) || fullyLoadedExtensionsByExtensionName.contains(extensionName); + } + + private boolean isFullyLoadedExtension(String extensionName) { + return fullyLoadedExtensions || fullyLoadedExtensionsByExtensionName.contains(extensionName); + } + + private boolean isRemovedAttributes(String id, String extensionName) { + return removedResources.contains(id) || removedExtensionAttributes.containsKey(id) && removedExtensionAttributes.get(id).contains(extensionName); + } + + private boolean isExtensionAttributesCached(String id, String extensionName) { + return resources.containsKey(id) && getCachedExtensionAttributes(id).containsKey(extensionName); + } + + /** + * Add extension attributes in the cache for single extension attributes loading + */ + private void addExtensionAttributesToCache(String identifiableId, String extensionName, ExtensionAttributes extensionAttributes) { + Objects.requireNonNull(extensionAttributes); + + getCachedExtensionAttributes(identifiableId).put(extensionName, extensionAttributes); + Set extensions = removedExtensionAttributes.get(identifiableId); + if (extensions != null) { + extensions.remove(extensionName); + if (extensions.isEmpty()) { + removedExtensionAttributes.remove(identifiableId); + } + } + } + + /** + * Get the extensions attributes with specified extension name for all the identifiables of the collection in the cache. + */ + public Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID networkUuid, int variantNum, ResourceType type, String extensionName) { + if (!isFullyLoadedExtension(extensionName)) { + // if collection has not yet been fully loaded we load it from the server + Map extensionAttributesMap = delegate.getAllExtensionsAttributesByResourceTypeAndExtensionName(networkUuid, variantNum, type, extensionName); + + // we update the full cache and set it as fully loaded + extensionAttributesMap.forEach((identifiableId, extensionAttributes) -> addExtensionAttributesToCache(identifiableId, extensionName, extensionAttributes)); + fullyLoadedExtensionsByExtensionName.add(extensionName); + } + return resources.entrySet() + .stream() + .filter(resourceEntry -> resourceEntry.getValue().getAttributes().getExtensionAttributes().containsKey(extensionName)) + .collect(Collectors.toMap(Map.Entry::getKey, resourceEntry -> resourceEntry.getValue().getAttributes().getExtensionAttributes().get(extensionName))); + } + + /** + * Get all extension attributes for one identifiable of the collection. + */ + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType type, String identifiableId) { + Objects.requireNonNull(identifiableId); + if (isExtensionAttributesCached(identifiableId)) { + return getCachedExtensionAttributes(identifiableId); + } + + if (!isFullyLoadedIdentifiable(identifiableId)) { + Map extensionAttributes = delegate.getAllExtensionsAttributesByIdentifiableId(networkUuid, variantNum, type, identifiableId); + if (extensionAttributes != null) { + addAllExtensionAttributesToCache(identifiableId, extensionAttributes); + return extensionAttributes; + } + } + return Map.of(); + } + + private boolean isFullyLoadedIdentifiable(String identifiableId) { + return fullyLoadedExtensions || fullyLoadedExtensionsByIdentifiableIds.contains(identifiableId); + } + + private boolean isExtensionAttributesCached(String identifiableId) { + return (fullyLoadedExtensionsByIdentifiableIds.contains(identifiableId) || fullyLoadedExtensions) && resources.containsKey(identifiableId); // am i sure? + } + + /** + * Add extension attributes to the cache when loading all the extension attributes of an identifiable + */ + private void addAllExtensionAttributesToCache(String id, Map extensionAttributes) { + Objects.requireNonNull(extensionAttributes); + + getCachedExtensionAttributes(id).putAll(extensionAttributes); + fullyLoadedExtensionsByIdentifiableIds.add(id); + removedExtensionAttributes.remove(id); + } + + /** + * Get all the extensions attributes for all the identifiables with specified resource type in the cache + */ + public Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType type) { + if (!fullyLoadedExtensions) { + // if collection has not yet been fully loaded we load it from the server + Map> extensionAttributesMap = delegate.getAllExtensionsAttributesByResourceType(networkUuid, variantNum, type); + + // we update the full cache and set it as fully loaded + extensionAttributesMap.forEach(this::addAllExtensionAttributesToCache); + fullyLoadedExtensions = true; + } + return resources.entrySet() + .stream() + .filter(resourceEntry -> !resourceEntry.getValue().getAttributes().getExtensionAttributes().isEmpty()) + .collect(Collectors.toMap(Map.Entry::getKey, resourceEntry -> resourceEntry.getValue().getAttributes().getExtensionAttributes())); + } + + public void removeExtensionAttributesByExtensionName(String identifiableId, String extensionName) { + Objects.requireNonNull(identifiableId); + Objects.requireNonNull(extensionName); + if (resources.containsKey(identifiableId)) { + getCachedExtensionAttributes(identifiableId).remove(extensionName); + removedExtensionAttributes.computeIfAbsent(identifiableId, k -> new HashSet<>()).add(extensionName); + } + } + + public void removeExtensionAttributesByIdentifiableId(String identifiableId) { + Objects.requireNonNull(identifiableId); + if (resources.containsKey(identifiableId)) { + Set removedExtensionNames = getCachedExtensionAttributes(identifiableId).keySet(); + removedExtensionAttributes.computeIfAbsent(identifiableId, k -> new HashSet<>()).addAll(removedExtensionNames); + } + } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkObjectIndex.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkObjectIndex.java index 4688575a2..ca0ebb1bc 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkObjectIndex.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkObjectIndex.java @@ -1218,4 +1218,16 @@ void updateSubstationResource(Resource resource, Attribute void updateBusbarSectionResource(Resource resource, AttributeFilter attributeFilter) { storeClient.updateBusbarSections(network.getUuid(), Collections.singletonList(resource), attributeFilter); } + + public void loadExtensionAttributes(ResourceType type, String identifiableId, String extensionName) { + storeClient.getExtensionAttributes(network.getUuid(), workingVariantNum, type, identifiableId, extensionName); + } + + public void loadAllExtensionsAttributesByIdentifiableId(ResourceType type, String identifiableId) { + storeClient.getAllExtensionsAttributesByIdentifiableId(network.getUuid(), workingVariantNum, type, identifiableId); + } + + public void removeExtensionAttributes(ResourceType type, String identifiableId, String extensionName) { + storeClient.removeExtensionAttributes(network.getUuid(), workingVariantNum, type, identifiableId, extensionName); + } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkStoreClient.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkStoreClient.java index bb26c8e84..785309dfc 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkStoreClient.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NetworkStoreClient.java @@ -10,6 +10,7 @@ import com.powsybl.network.store.model.*; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -301,6 +302,37 @@ public interface NetworkStoreClient { void updateTieLines(UUID networkUuid, List> tieLineResources, AttributeFilter attributeFilter); + // Extension Attributes + + /** + * For one identifiable with a specific identifiable id, retrieves one extension attributes by its extension name. + * @return {@link ExtensionAttributes} which is a subset of an identifiable resource. The extension attributes can be put in the + * extensionAttributes map of an {@link IdentifiableAttributes} or used to load an extension. + */ + Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName); + + /** + * For all the identifiables of a specific resource type, retrieves one extension attributes by its extension name. + * Used for preloading collection strategy. + * @return A {@link Map} where keys are identifiable IDs and values are {@link ExtensionAttributes}. + */ + Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID networkUuid, int variantNum, ResourceType resourceType, String extensionName); + + /** + * For one identifiable with a specific identifiable id, retrieves all extension attributes of this identifiable. + * @return A {@link Map} where keys are extension names and values are {@link ExtensionAttributes}. + */ + Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId); + + /** + * For all the identifiables of a specific resource type, retrieves all extension attributes of this identifiable. + * Used for preloading collection strategy. + * @return A {@link Map} where keys are identifiable IDs and values are {@link Map}s where keys are extension names and values are {@link ExtensionAttributes}. + */ + Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType); + + void removeExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName); + Optional> getIdentifiable(UUID networkUuid, int variantNum, String id); List getIdentifiablesIds(UUID networkUuid, int variantNum); diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/OfflineNetworkStoreClient.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/OfflineNetworkStoreClient.java index db8eae967..fa3d46323 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/OfflineNetworkStoreClient.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/OfflineNetworkStoreClient.java @@ -8,10 +8,7 @@ import com.powsybl.network.store.model.*; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; /** * @author Geoffroy Jamgotchian @@ -609,6 +606,31 @@ public void flush(UUID networkUuid) { // nothing to do } + @Override + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + return Optional.empty(); + } + + @Override + public Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID uuid, int variantNum, ResourceType resourceType, String extensionName) { + return Map.of(); + } + + @Override + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId) { + return Map.of(); + } + + @Override + public Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) { + return Map.of(); + } + + @Override + public void removeExtensionAttributes(UUID uuid, int workingVariantNum, ResourceType resourceType, String identifiableId, String extensionName) { + // nothing to do + } + @Override public List> getVoltageLevelGrounds(UUID networkUuid, int variantNum, String voltageLevelId) { diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/CollectionCacheTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/CollectionCacheTest.java index 325b6f78c..301dcf693 100644 --- a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/CollectionCacheTest.java +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/CollectionCacheTest.java @@ -8,8 +8,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.network.store.iidm.impl.util.TriFunction; -import com.powsybl.network.store.model.LoadAttributes; -import com.powsybl.network.store.model.Resource; +import com.powsybl.network.store.model.*; import org.junit.Before; import org.junit.Test; @@ -31,6 +30,11 @@ public class CollectionCacheTest { private boolean oneLoaderCalled; private boolean containerLoaderCalled; private boolean allLoaderCalled; + MockNetworkStoreClient mockNetworkStoreClient; + + private ActivePowerControlAttributes apc1; + private ActivePowerControlAttributes apc2; + private OperatingStatusAttributes os1; private Resource l1; private Resource l2; @@ -72,6 +76,22 @@ public void setUp() { .build()) .build(); + apc1 = ActivePowerControlAttributes.builder() + .droop(5.2) + .participate(true) + .participationFactor(0.5) + .build(); + + apc2 = ActivePowerControlAttributes.builder() + .droop(6) + .participate(false) + .participationFactor(0.5) + .build(); + + os1 = OperatingStatusAttributes.builder() + .operatingStatus("foo") + .build(); + oneLoader = (networkUuid, variantNum, id) -> { oneLoaderCalled = true; if (id.equals("l1")) { @@ -100,8 +120,8 @@ public void setUp() { allLoaderCalled = true; return Arrays.asList(l1, l2, l3); }; - - collectionCache = new CollectionCache<>(oneLoader, containerLoader, allLoader); + mockNetworkStoreClient = new MockNetworkStoreClient(apc1, apc2, os1); + collectionCache = new CollectionCache<>(oneLoader, containerLoader, allLoader, mockNetworkStoreClient); } @Test @@ -149,7 +169,7 @@ public void getResourceFirstTest() { @Test public void incorrectGetContainerResourcesTest() { - CollectionCache otherCollectionCache = new CollectionCache<>(oneLoader, null, allLoader); + CollectionCache otherCollectionCache = new CollectionCache<>(oneLoader, null, allLoader, null); PowsyblException exception = assertThrows(PowsyblException.class, () -> otherCollectionCache.getContainerResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "")); assertEquals("it is not possible to load resources by container, if container resources loader has not been specified", exception.getMessage()); } @@ -321,4 +341,217 @@ public void removeResourceThenGetContainerTest() { collectionCache.getContainerResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "vl1"); assertEquals(1, collectionCache.getContainerResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "vl1").size()); } + + @Test + public void getExtensionAttributesWithResourceNotCachedMustThrow() { + PowsyblException exception = assertThrows(PowsyblException.class, () -> collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl")); + assertTrue(exception.getMessage().startsWith("Cannot manipulate extensions for identifiable")); + exception = assertThrows(PowsyblException.class, () -> collectionCache.getAllExtensionsAttributesByResourceTypeAndExtensionName(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "activePowerControl")); + assertTrue(exception.getMessage().startsWith("Cannot manipulate extensions for identifiable")); + exception = assertThrows(PowsyblException.class, () -> collectionCache.getAllExtensionsAttributesByIdentifiableId(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1")); + assertTrue(exception.getMessage().startsWith("Cannot manipulate extensions for identifiable")); + exception = assertThrows(PowsyblException.class, () -> collectionCache.getAllExtensionsAttributesByResourceType(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD)); + assertTrue(exception.getMessage().startsWith("Cannot manipulate extensions for identifiable")); + } + + @Test + public void getExtensionAttributes() { + // Load resources in cache + assertEquals(l1, collectionCache.getResource(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "l1").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertTrue(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributeLoaderCalled(false); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void removeExtensionAttributes() { + // Load resources in cache + assertEquals(Arrays.asList(l1, l2, l3), collectionCache.getResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + // Remove the extension by name then try to retrieve it. There should be no call to loader + collectionCache.removeExtensionAttributesByExtensionName("l1", "activePowerControl"); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + // Remove the resource by id then try to retrieve it. There should be no call to loader + collectionCache.removeResource("l2"); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l2", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void extensionIsCachedAndResourceIsRemovedAndAddedAgainWithoutExtensions() { + // Load resources in cache + assertEquals(l1, collectionCache.getResource(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "l1").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertTrue(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributeLoaderCalled(false); + // Remove the resource by id then try to retrieve it. There should be no call to loader + collectionCache.removeResource("l1"); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + Resource l1WithoutExtensions = Resource.loadBuilder() + .id("l1") + .attributes(LoadAttributes.builder() + .voltageLevelId("vl1") + .build()) + .build(); + collectionCache.addResource(l1WithoutExtensions); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void extensionIsCachedAndResourceIsRemovedAndAddedAgainWithDifferentExtensions() { + // Load resources in cache + assertEquals(l1, collectionCache.getResource(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "l1").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertTrue(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributeLoaderCalled(false); + // Remove the resource by id then try to retrieve it. There should be no call to loader + collectionCache.removeResource("l1"); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + l1.getAttributes().getExtensionAttributes().put("activePowerControl", apc2); + collectionCache.addResource(l1); + assertEquals(apc2, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void whenExtensionIsCachedThenRemovedClearExtensionCache() { + // Load resources in cache + assertEquals(l1, collectionCache.getResource(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, "l1").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertTrue(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributeLoaderCalled(false); + // Remove the resource by id then try to retrieve it. There should be no call to loader + collectionCache.removeExtensionAttributesByExtensionName("l1", "activePowerControl"); + assertNull(collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void getExtensionAttributesLoaderByResourceTypeAndName() { + // Load resources in cache + assertEquals(Arrays.asList(l1, l2, l3), collectionCache.getResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(Map.of("l1", apc1, "l2", apc2), collectionCache.getAllExtensionsAttributesByResourceTypeAndExtensionName(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "activePowerControl")); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertTrue(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributesLoaderByResourceTypeAndNameCalled(false); + assertEquals(Map.of("l1", apc1, "l2", apc2), collectionCache.getAllExtensionsAttributesByResourceTypeAndExtensionName(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "activePowerControl")); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertEquals(apc2, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l2", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void getExtensionAttributesLoaderById() { + // Load resources in cache + assertEquals(Arrays.asList(l1, l2, l3), collectionCache.getResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(Map.of("activePowerControl", apc1, "operatingStatus", os1), collectionCache.getAllExtensionsAttributesByIdentifiableId(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1")); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertTrue(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertEquals(os1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "operatingStatus").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertTrue(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } + + @Test + public void getExtensionAttributesLoaderByResourceType() { + // Load resources in cache + assertEquals(Arrays.asList(l1, l2, l3), collectionCache.getResources(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + assertEquals(Map.of("l1", Map.of("activePowerControl", apc1, "operatingStatus", os1), "l2", Map.of("activePowerControl", apc2)), collectionCache.getAllExtensionsAttributesByResourceType(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertTrue(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + mockNetworkStoreClient.setExtensionAttributesLoaderByResourceTypeCalled(false); + assertEquals(Map.of("l1", Map.of("activePowerControl", apc1, "operatingStatus", os1), "l2", Map.of("activePowerControl", apc2)), collectionCache.getAllExtensionsAttributesByResourceType(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD)); + assertEquals(apc1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "activePowerControl").orElse(null)); + assertEquals(os1, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l1", "operatingStatus").orElse(null)); + assertEquals(apc2, collectionCache.getExtensionAttributes(NETWORK_UUID, Resource.INITIAL_VARIANT_NUM, ResourceType.LOAD, "l2", "activePowerControl").orElse(null)); + assertFalse(mockNetworkStoreClient.isExtensionAttributeLoaderCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeAndNameCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByIdCalled()); + assertFalse(mockNetworkStoreClient.isExtensionAttributesLoaderByResourceTypeCalled()); + } } diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/MockNetworkStoreClient.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/MockNetworkStoreClient.java new file mode 100644 index 000000000..03316e2d4 --- /dev/null +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/MockNetworkStoreClient.java @@ -0,0 +1,706 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.iidm.impl; + +import com.powsybl.network.store.model.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author Antoine Bouhours + */ +@Getter +@Setter +class MockNetworkStoreClient implements NetworkStoreClient { + private final ActivePowerControlAttributes apc1; + private final ActivePowerControlAttributes apc2; + private final OperatingStatusAttributes os1; + private boolean extensionAttributeLoaderCalled = false; + private boolean extensionAttributesLoaderByResourceTypeAndNameCalled = false; + private boolean extensionAttributesLoaderByIdCalled = false; + private boolean extensionAttributesLoaderByResourceTypeCalled = false; + + public MockNetworkStoreClient(ActivePowerControlAttributes apc1, ActivePowerControlAttributes apc2, OperatingStatusAttributes os1) { + this.apc1 = apc1; + this.apc2 = apc2; + this.os1 = os1; + } + + // Methods used in tests + @Override + public Optional getExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + extensionAttributeLoaderCalled = true; + if (identifiableId.equals("l1") && extensionName.equals("operatingStatus")) { + return Optional.of(os1); + } else if (identifiableId.equals("l1") && extensionName.equals("activePowerControl")) { + return Optional.of(apc1); + } else if (identifiableId.equals("l2") && extensionName.equals("activePowerControl")) { + return Optional.of(apc2); + } + return Optional.empty(); + } + + @Override + public Map getAllExtensionsAttributesByResourceTypeAndExtensionName(UUID networkUuid, int variantNum, ResourceType resourceType, String extensionName) { + extensionAttributesLoaderByResourceTypeAndNameCalled = true; + if (extensionName.equals("activePowerControl")) { + return Map.of("l1", apc1, "l2", apc2); + } + return Map.of(); + } + + @Override + public Map getAllExtensionsAttributesByIdentifiableId(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId) { + extensionAttributesLoaderByIdCalled = true; + if (identifiableId.equals("l1")) { + return Map.of("activePowerControl", apc1, "operatingStatus", os1); + } else if (identifiableId.equals("l2")) { + return Map.of("activePowerControl", apc2); + } + return Map.of(); + } + + @Override + public Map> getAllExtensionsAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) { + extensionAttributesLoaderByResourceTypeCalled = true; + if (resourceType == ResourceType.LOAD) { + return Map.of("l1", Map.of("activePowerControl", apc1, "operatingStatus", os1), "l2", Map.of("activePowerControl", apc2)); + } + return Map.of(); + } + + // Methods below are not used in tests + @Override + public void removeExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List getNetworksInfos() { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createNetworks(List> networkResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List getVariantsInfos(UUID networkUuid) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getNetwork(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void deleteNetwork(UUID networkUuid) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void deleteNetwork(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateNetworks(List> networkResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void cloneNetwork(UUID networkUuid, int sourceVariantNum, int targetVariantNum, String targetVariantId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void cloneNetwork(UUID networkUuid, String sourceVariantId, String targetVariantId, boolean mayOverwrite) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void cloneNetwork(UUID networkUuid, UUID sourceNetworkUuid, List targetVariantIds) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createSubstations(UUID networkUuid, List> substationResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getSubstations(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getSubstation(UUID networkUuid, int variantNum, String substationId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateSubstations(UUID networkUuid, List> substationResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeSubstations(UUID networkUuid, int variantNum, List substationsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createVoltageLevels(UUID networkUuid, List> voltageLevelResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getVoltageLevel(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevels(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelsInSubstation(UUID networkUuid, int variantNum, String substationId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateVoltageLevels(UUID networkUuid, List> voltageLevelResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeVoltageLevels(UUID networkUuid, int variantNum, List voltageLevelsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelBusbarSections(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelSwitches(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelGenerators(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelBatteries(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelLoads(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelShuntCompensators(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelVscConverterStations(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelStaticVarCompensators(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelLccConverterStations(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelTwoWindingsTransformers(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelThreeWindingsTransformers(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelLines(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelDanglingLines(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelGrounds(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVoltageLevelConfiguredBuses(UUID networkUuid, int variantNum, String voltageLevelId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createSwitches(UUID networkUuid, List> switchResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getSwitches(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getSwitch(UUID networkUuid, int variantNum, String switchId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateSwitches(UUID networkUuid, List> switchResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeSwitches(UUID networkUuid, int variantNum, List switchesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createBusbarSections(UUID networkUuid, List> busbarSectionResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getBusbarSections(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getBusbarSection(UUID networkUuid, int variantNum, String busbarSectionId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateBusbarSections(UUID networkUuid, List> busbarSectionResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeBusBarSections(UUID networkUuid, int variantNum, List busBarSectionsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createLoads(UUID networkUuid, List> loadResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getLoads(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getLoad(UUID networkUuid, int variantNum, String loadId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateLoads(UUID networkUuid, List> loadResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeLoads(UUID networkUuid, int variantNum, List loadsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createGenerators(UUID networkUuid, List> generatorResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getGenerators(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getGenerator(UUID networkUuid, int variantNum, String generatorId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateGenerators(UUID networkUuid, List> generatorResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeGenerators(UUID networkUuid, int variantNum, List generatorsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createBatteries(UUID networkUuid, List> batteryResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getBatteries(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getBattery(UUID networkUuid, int variantNum, String batteryId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateBatteries(UUID networkUuid, List> batteryResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeBatteries(UUID networkUuid, int variantNum, List batteriesIds) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createTwoWindingsTransformers(UUID networkUuid, List> twoWindingsTransformerResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getTwoWindingsTransformers(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getTwoWindingsTransformer(UUID networkUuid, int variantNum, String twoWindingsTransformerId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateTwoWindingsTransformers(UUID networkUuid, List> twoWindingsTransformerResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeTwoWindingsTransformers(UUID networkUuid, int variantNum, List twoWindingsTransformersId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createThreeWindingsTransformers(UUID networkUuid, List> threeWindingsTransformerResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getThreeWindingsTransformers(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getThreeWindingsTransformer(UUID networkUuid, int variantNum, String threeWindingsTransformerId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateThreeWindingsTransformers(UUID networkUuid, List> threeWindingsTransformerResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeThreeWindingsTransformers(UUID networkUuid, int variantNum, List threeWindingsTransformersId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createLines(UUID networkUuid, List> lineResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getLines(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getLine(UUID networkUuid, int variantNum, String lineId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateLines(UUID networkUuid, List> lineResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeLines(UUID networkUuid, int variantNum, List linesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createShuntCompensators(UUID networkUuid, List> shuntCompensatorResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getShuntCompensators(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getShuntCompensator(UUID networkUuid, int variantNum, String shuntCompensatorId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateShuntCompensators(UUID networkUuid, List> shuntCompensatorResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeShuntCompensators(UUID networkUuid, int variantNum, List shuntCompensatorsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createVscConverterStations(UUID networkUuid, List> vscConverterStationResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getVscConverterStations(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getVscConverterStation(UUID networkUuid, int variantNum, String vscConverterStationId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateVscConverterStations(UUID networkUuid, List> vscConverterStationResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeVscConverterStations(UUID networkUuid, int variantNum, List vscConverterStationsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createLccConverterStations(UUID networkUuid, List> lccConverterStationResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getLccConverterStations(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getLccConverterStation(UUID networkUuid, int variantNum, String lccConverterStationId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateLccConverterStations(UUID networkUuid, List> lccConverterStationResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeLccConverterStations(UUID networkUuid, int variantNum, List lccConverterStationsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createStaticVarCompensators(UUID networkUuid, List> svcResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getStaticVarCompensators(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getStaticVarCompensator(UUID networkUuid, int variantNum, String staticVarCompensatorId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateStaticVarCompensators(UUID networkUuid, List> staticVarCompensatorResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeStaticVarCompensators(UUID networkUuid, int variantNum, List staticVarCompensatorsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createHvdcLines(UUID networkUuid, List> hvdcLineResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getHvdcLines(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getHvdcLine(UUID networkUuid, int variantNum, String hvdcLineId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeHvdcLines(UUID networkUuid, int variantNum, List hvdcLinesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateHvdcLines(UUID networkUuid, List> hvdcLineResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createDanglingLines(UUID networkUuid, List> danglingLineResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getDanglingLines(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getDanglingLine(UUID networkUuid, int variantNum, String danglingLineId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeDanglingLines(UUID networkUuid, int variantNum, List danglingLinesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateDanglingLines(UUID networkUuid, List> danglingLineResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createGrounds(UUID networkUuid, List> groundResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getGrounds(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getGround(UUID networkUuid, int variantNum, String groundId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeGrounds(UUID networkUuid, int variantNum, List groundsId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateGrounds(UUID networkUuid, List> groundResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createConfiguredBuses(UUID networkUuid, List> busesRessources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getConfiguredBuses(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getConfiguredBus(UUID networkUuid, int variantNum, String busId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateConfiguredBuses(UUID networkUuid, List> busesResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeConfiguredBuses(UUID networkUuid, int variantNum, List busesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void createTieLines(UUID networkUuid, List> tieLineResources) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List> getTieLines(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getTieLine(UUID networkUuid, int variantNum, String tieLineId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void removeTieLines(UUID networkUuid, int variantNum, List tieLinesId) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void updateTieLines(UUID networkUuid, List> tieLineResources, AttributeFilter attributeFilter) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public Optional> getIdentifiable(UUID networkUuid, int variantNum, String id) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public List getIdentifiablesIds(UUID networkUuid, int variantNum) { + throw new UnsupportedOperationException("Unimplemented method"); + } + + @Override + public void flush(UUID networkUuid) { + throw new UnsupportedOperationException("Unimplemented method"); + } +} diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionAttributesIdResolver.java b/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionAttributesIdResolver.java index 63841da98..4e51fdf5d 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionAttributesIdResolver.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionAttributesIdResolver.java @@ -39,7 +39,7 @@ public String idFromValueAndType(Object obj, Class subType) { @Override public JavaType typeFromId(DatabindContext context, String id) { - Class subType = ExtensionLoaders.findLoader(id).getAttributesType(); + Class subType = ExtensionLoaders.findLoaderByName(id).getAttributesType(); return context.constructSpecializedType(superType, subType); } } diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionLoaders.java b/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionLoaders.java index 413c0cb4d..99b04866b 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionLoaders.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/ExtensionLoaders.java @@ -7,6 +7,7 @@ package com.powsybl.network.store.model; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.extensions.Extension; import com.powsybl.commons.util.ServiceLoaderCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,21 +18,52 @@ /** * @author Antoine Bouhours */ +//Don't bother with generics because serviceloader doesn't return them +//and we put them in a cache where we can't propagate the generic types. +@SuppressWarnings("rawtypes") public final class ExtensionLoaders { private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionLoaders.class); private static final ServiceLoaderCache EXTENSION_LOADERS = new ServiceLoaderCache<>(ExtensionLoader.class); + private static Predicate namePredicate(String name) { + return s -> s.getName() != null && name.equals(s.getName()); + } + + private static Predicate typePredicate(Class type) { + return s -> type == s.getType(); + } + + private static Predicate attributesTypePredicate(Class attributesType) { + return s -> attributesType.isAssignableFrom(s.getAttributesType()); + } + private ExtensionLoaders() { } - public static ExtensionLoader findLoader(String name) { - return findLoader(s -> s.getName() != null && name.equals(s.getName()), name); + public static boolean loaderExists(String name) { + return loaderExists(namePredicate(name)); + } + + public static boolean loaderExists(Class type) { + return loaderExists(typePredicate(type)); + } + + private static boolean loaderExists(Predicate predicate) { + return EXTENSION_LOADERS.getServices().stream().anyMatch(predicate); + } + + public static ExtensionLoader findLoaderByName(String name) { + return findLoader(namePredicate(name), name); + } + + public static ExtensionLoader findLoader(Class type) { + return findLoader(typePredicate(type), type.getSimpleName()); } - public static ExtensionLoader findLoaderByAttributes(Class type) { - return findLoader(s -> type.isAssignableFrom(s.getAttributesType()), type.getSimpleName()); + public static ExtensionLoader findLoaderByAttributes(Class attributesType) { + return findLoader(attributesTypePredicate(attributesType), attributesType.getSimpleName()); } private static ExtensionLoader findLoader( diff --git a/network-store-model/src/test/java/com/powsybl/network/store/model/LoaderTest.java b/network-store-model/src/test/java/com/powsybl/network/store/model/LoaderTest.java index 5ba845364..1205c57d0 100644 --- a/network-store-model/src/test/java/com/powsybl/network/store/model/LoaderTest.java +++ b/network-store-model/src/test/java/com/powsybl/network/store/model/LoaderTest.java @@ -7,10 +7,10 @@ package com.powsybl.network.store.model; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.extensions.Extension; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.Assert.*; /** * @author Antoine Bouhours @@ -18,13 +18,21 @@ public class LoaderTest { @Test public void testLoaderNotFound() { - PowsyblException exception = assertThrows(PowsyblException.class, () -> ExtensionLoaders.findLoader("unknown")); + PowsyblException exception = assertThrows(PowsyblException.class, () -> ExtensionLoaders.findLoaderByName("unknown")); assertEquals("ExtensionLoader not found", exception.getMessage()); } + @Test + public void testLoaderExists() { + assertTrue(ExtensionLoaders.loaderExists("loader")); + assertFalse(ExtensionLoaders.loaderExists("unknown")); + assertTrue(ExtensionLoaders.loaderExists(Extension.class)); + assertFalse(ExtensionLoaders.loaderExists(Object.class)); + } + @Test public void testDuplicatedLoader() { - PowsyblException exception = assertThrows(PowsyblException.class, () -> ExtensionLoaders.findLoader("loader")); + PowsyblException exception = assertThrows(PowsyblException.class, () -> ExtensionLoaders.findLoaderByName("loader")); assertEquals("Multiple ExtensionLoaders configuration providers found", exception.getMessage()); } } diff --git a/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader1.java b/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader1.java index dc39532d4..282ae0adf 100644 --- a/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader1.java +++ b/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader1.java @@ -28,8 +28,8 @@ public String getName() { } @Override - public Class> getType() { - return null; + public Class getType() { + return Extension.class; } @Override diff --git a/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader2.java b/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader2.java index e58524698..2d7c37e08 100644 --- a/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader2.java +++ b/network-store-model/src/test/java/com/powsybl/network/store/model/tools/DuplicatedLoader2.java @@ -28,8 +28,8 @@ public String getName() { } @Override - public Class> getType() { - return null; + public Class getType() { + return Extension.class; } @Override