diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java new file mode 100644 index 000000000..db6ba86bb --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.client; + +import static org.junit.Assert.*; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.IntStream; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.Key; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.junit.Test; + +public abstract class ConnectedAasManagerMultithreadingTestSuite { + static final int N_THREADS = 20; + + protected abstract AasRepository getAasRepository(); + + protected abstract ConnectedAasManager getConnectedAasManager(); + + @Test + public void testParallelSubmodelCreation() throws ExecutionException, InterruptedException { + AssetAdministrationShell shell = createShell(); + + ExecutorService executorService = Executors.newFixedThreadPool(N_THREADS); + ConcurrentLinkedDeque createdSubmodelIds = new ConcurrentLinkedDeque<>(); + + List> futures = IntStream.range(0, N_THREADS).mapToObj(i -> executorService.submit(() -> createdSubmodelIds.add(createSubmodel(shell.getId(), i)))).toList(); + + try { + for (int i = 0; i < N_THREADS; i++) { + futures.get(i).get(); + } + } finally { + executorService.shutdown(); + } + + createdSubmodelIds.forEach(submodelId -> assertSubmodelWasCreatedAndRegistered(shell.getId(), submodelId)); + } + + void assertSubmodelWasCreatedAndRegistered(String shellId, String submodelId) { + assertEquals("No submodel with id " + submodelId + " found by the client", submodelId, getConnectedAasManager().getSubmodelService(submodelId).getSubmodel().getId()); + assertTrue("SubmodelRef " + submodelId + " not found in shell " + shellId, + getAasRepository().getSubmodelReferences(shellId, PaginationInfo.NO_LIMIT).getResult().stream().map(Reference::getKeys).flatMap(Collection::stream).map(Key::getValue).anyMatch(submodelId::equals)); + } + + private AssetAdministrationShell createShell() { + String id = UUID.randomUUID().toString(); + DefaultAssetAdministrationShell shell = new DefaultAssetAdministrationShell.Builder().id(id).build(); + getConnectedAasManager().createAas(shell); + return getConnectedAasManager().getAasService(id).getAAS(); + } + + private String createSubmodel(String aasId, int threadId) { + try { + String id = aasId + "-thread" + threadId; + DefaultSubmodel submodel = new DefaultSubmodel.Builder().id(id).build(); + getConnectedAasManager().createSubmodelInAas(aasId, submodel); + return id; + } catch (Exception e) { + throw new RuntimeException("Failed at thread " + threadId, e); + } + } + +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreading.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingInMemory.java similarity index 55% rename from basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreading.java rename to basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingInMemory.java index 8ff0ea860..4b86934f6 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreading.java +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingInMemory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,60 +25,33 @@ package org.eclipse.digitaltwin.basyx.aasenvironment.client; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.stream.IntStream; - -import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; -import org.eclipse.digitaltwin.aas4j.v3.model.Key; -import org.eclipse.digitaltwin.aas4j.v3.model.Reference; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; -import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.eclipse.digitaltwin.basyx.submodelrepository.client.ConnectedSubmodelRepository; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; -public class TestConnectedAasManagerMultithreading { +public class TestConnectedAasManagerMultithreadingInMemory extends ConnectedAasManagerMultithreadingTestSuite { static final String AAS_REPOSITORY_BASE_PATH = "http://localhost:8081"; static final String SM_REPOSITORY_BASE_PATH = "http://localhost:8081"; static final String AAS_REGISTRY_BASE_PATH = "http://localhost:8050"; static final String SM_REGISTRY_BASE_PATH = "http://localhost:8060"; - static final int N_THREADS = 20; - - static ConfigurableApplicationContext appContext; - static AasRepository aasRepository; - static SubmodelRepository smRepository; + static ConnectedAasManager aasManager; static ConnectedAasRepository connectedAasRepository; static ConnectedSubmodelRepository connectedSmRepository; static RegistryAndDiscoveryInterfaceApi aasRegistryApi; static SubmodelRegistryApi smRegistryApi; - - static ConnectedAasManager aasManager; + static ConfigurableApplicationContext appContext; @BeforeClass public static void setupRepositories() { appContext = new SpringApplication(DummyAasEnvironmentComponent.class).run(new String[] {}); - connectedAasRepository = new ConnectedAasRepository(AAS_REPOSITORY_BASE_PATH); connectedSmRepository = new ConnectedSubmodelRepository(SM_REPOSITORY_BASE_PATH); aasRegistryApi = new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH); @@ -98,32 +71,6 @@ public static void stopContext() { appContext.close(); } - @Test - public void testParallelSubmodelCreation() throws ExecutionException, InterruptedException { - AssetAdministrationShell shell = createShell(); - - ExecutorService executorService = Executors.newFixedThreadPool(N_THREADS); - ConcurrentLinkedDeque createdSubmodelIds = new ConcurrentLinkedDeque<>(); - - List> futures = IntStream.range(0, N_THREADS).mapToObj(i -> executorService.submit(() -> createdSubmodelIds.add(createSubmodel(shell.getId(), i)))).toList(); - - try { - for (int i = 0; i < N_THREADS; i++) { - futures.get(i).get(); - } - } finally { - executorService.shutdown(); - } - - createdSubmodelIds.forEach(submodelId -> assertSubmodelWasCreatedAndRegistered(shell.getId(), submodelId)); - } - - static void assertSubmodelWasCreatedAndRegistered(String shellId, String submodelId) { - assertEquals(submodelId, aasManager.getSubmodelService(submodelId).getSubmodel().getId()); - assertTrue(connectedAasRepository.getSubmodelReferences(shellId, PaginationInfo.NO_LIMIT).getResult().stream().map(Reference::getKeys).flatMap(Collection::stream).map(Key::getValue).anyMatch(submodelId::equals)); - } - - private static void cleanUpRegistries() { try { aasRegistryApi.deleteAllShellDescriptors(); @@ -137,22 +84,13 @@ private static void cleanUpRegistries() { } } - private static AssetAdministrationShell createShell() { - String id = UUID.randomUUID().toString(); - DefaultAssetAdministrationShell shell = new DefaultAssetAdministrationShell.Builder().id(id).build(); - aasManager.createAas(shell); - return aasManager.getAasService(id).getAAS(); + @Override + public AasRepository getAasRepository() { + return connectedAasRepository; } - private static String createSubmodel(String aasId, int threadId) { - try { - String id = aasId + "-thread" + threadId; - DefaultSubmodel submodel = new DefaultSubmodel.Builder().id(id).build(); - aasManager.createSubmodelInAas(aasId, submodel); - return id; - } catch (Exception e) { - throw new RuntimeException("Failed at thread " + threadId, e); - } + @Override + public ConnectedAasManager getConnectedAasManager() { + return aasManager; } - } diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingMongoDb.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingMongoDb.java new file mode 100644 index 000000000..1dac2127c --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreadingMongoDb.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.client; + +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelrepository.client.ConnectedSubmodelRepository; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ConfigurableApplicationContext; + +public class TestConnectedAasManagerMultithreadingMongoDb extends ConnectedAasManagerMultithreadingTestSuite { + static final String AAS_REPOSITORY_BASE_PATH = "http://localhost:8081"; + static final String SM_REPOSITORY_BASE_PATH = "http://localhost:8081"; + static final String AAS_REGISTRY_BASE_PATH = "http://localhost:8050"; + static final String SM_REGISTRY_BASE_PATH = "http://localhost:8060"; + + static ConnectedAasManager aasManager; + static ConnectedAasRepository connectedAasRepository; + static ConnectedSubmodelRepository connectedSmRepository; + static RegistryAndDiscoveryInterfaceApi aasRegistryApi; + static SubmodelRegistryApi smRegistryApi; + static ConfigurableApplicationContext appContext; + + @BeforeClass + public static void setupRepositories() { + appContext = new SpringApplicationBuilder(DummyAasEnvironmentComponent.class).profiles("mongodb").run(new String[] {}); + connectedAasRepository = new ConnectedAasRepository(AAS_REPOSITORY_BASE_PATH); + connectedSmRepository = new ConnectedSubmodelRepository(SM_REPOSITORY_BASE_PATH); + aasRegistryApi = new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH); + smRegistryApi = new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH); + aasManager = new ConnectedAasManager(AAS_REGISTRY_BASE_PATH, AAS_REPOSITORY_BASE_PATH, SM_REGISTRY_BASE_PATH, SM_REPOSITORY_BASE_PATH); + + cleanUpRegistries(); + } + + @After + public void cleanUpComponents() { + cleanUpRegistries(); + } + + @AfterClass + public static void stopContext() { + appContext.close(); + } + + private static void cleanUpRegistries() { + try { + aasRegistryApi.deleteAllShellDescriptors(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + try { + smRegistryApi.deleteAllSubmodelDescriptors(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + @Override + public AasRepository getAasRepository() { + return connectedAasRepository; + } + + @Override + public ConnectedAasManager getConnectedAasManager() { + return aasManager; + } +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-mongodb.properties b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-mongodb.properties new file mode 100644 index 000000000..4afb4e37e --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-mongodb.properties @@ -0,0 +1,8 @@ +basyx.backend = MongoDB + +spring.data.mongodb.host=127.0.0.1 +spring.data.mongodb.port=27017 +spring.data.mongodb.database=aas-env +spring.data.mongodb.authentication-database=admin +spring.data.mongodb.username=mongoAdmin +spring.data.mongodb.password=mongoPassword \ No newline at end of file diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java index c3fd3001b..662941afd 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -36,8 +36,7 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudConceptDescriptionRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.InMemoryAasRepositoryBackend; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; @@ -77,7 +76,7 @@ public class AasEnvironmentLoaderTest { @Before public void setUp() { submodelRepository = Mockito.spy(new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository()))); - aasRepository = Mockito.spy(new CrudAasRepository(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository()))); + aasRepository = Mockito.spy(new CrudAasRepository(InMemoryAasRepositoryBackend.buildDefault(), "aas-repo")); conceptDescriptionRepository = Mockito.spy(new CrudConceptDescriptionRepository(new ConceptDescriptionInMemoryBackendProvider())); } diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java index 0564b0d4a..c4dafee30 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.aasenvironment; +import static org.junit.Assert.*; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -52,10 +54,9 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; import org.eclipse.digitaltwin.basyx.aasenvironment.base.DefaultAASEnvironment; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleConceptDescriptionRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.InMemoryAasRepositoryBackend; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; @@ -70,8 +71,6 @@ import org.junit.Test; import org.xml.sax.SAXException; -import static org.junit.Assert.*; - public class TestAASEnvironmentSerialization { public static final String AAS_TECHNICAL_DATA_ID = "shell001"; @@ -88,7 +87,7 @@ public class TestAASEnvironmentSerialization { @Before public void setup() { submodelRepository = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())).create(); - aasRepository = new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository())).create(); + aasRepository = new CrudAasRepositoryFactory(InMemoryAasRepositoryBackend.buildDefault(), "aas-repo").create(); conceptDescriptionRepository = new SimpleConceptDescriptionRepositoryFactory(new ConceptDescriptionInMemoryBackendProvider(), createDummyConceptDescriptions(), "cdRepo").create(); for (Submodel submodel : createDummySubmodels()) { diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/pom.xml b/basyx.aasenvironment/basyx.aasenvironment.component/pom.xml index ed64a86ec..84bcb83e4 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/pom.xml +++ b/basyx.aasenvironment/basyx.aasenvironment.component/pom.xml @@ -117,8 +117,12 @@ httpclient5 test + + org.springframework.boot + spring-boot-starter-test + test + - diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/MongoDbCollectionsTestConfig.java b/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/MongoDbCollectionsTestConfig.java index 41ef118df..fabf84edf 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/MongoDbCollectionsTestConfig.java +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/MongoDbCollectionsTestConfig.java @@ -1,12 +1,38 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + package org.eclipse.digitaltwin.basyx.aasenvironment.component; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.MongoTemplate; -@Configuration +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; + +@TestConfiguration public class MongoDbCollectionsTestConfig { protected static final String CONNECTION_URL = "mongodb://mongoAdmin:mongoPassword@localhost:27017/"; protected static final String DB_NAME = "aas-env"; @@ -15,7 +41,7 @@ public class MongoDbCollectionsTestConfig { protected static final String CD_REPO_COLLECTION = "cd-repo"; @Bean - public static MongoTemplate buildMongoTemplate() { + MongoTemplate buildMongoTemplate() { MongoClient client = MongoClients.create(CONNECTION_URL); return new MongoTemplate(client, DB_NAME); } diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/TestMongoDbCollections.java b/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/TestMongoDbCollections.java index 8bb416517..d04c40e48 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/TestMongoDbCollections.java +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/TestMongoDbCollections.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,44 +25,27 @@ package org.eclipse.digitaltwin.basyx.aasenvironment.component; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; -import org.eclipse.digitaltwin.basyx.common.mongocore.MongoDBUtilities; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; +import org.springframework.test.context.junit4.SpringRunner; /** - * TestMongoDbCollections + * Tests if MongoDB collections are correctly defined after initialization * * @author mateusmolina * */ +@SpringBootTest(properties = "spring.profiles.active=mongodb") +@RunWith(SpringRunner.class) public class TestMongoDbCollections { - private static ConfigurableApplicationContext appContext; - - // MongoDB configuration - - private static final MongoTemplate mongoTemplate = MongoDbCollectionsTestConfig.buildMongoTemplate(); - @BeforeClass - public static void startAASEnvironment() throws Exception { - appContext = new SpringApplicationBuilder(AasEnvironmentComponent.class).profiles("mongodb").run(new String[] {}); - } - - @AfterClass - public static void deleteDatabase() { - appContext.close(); - MongoDBUtilities.clearCollection(mongoTemplate, MongoDbCollectionsTestConfig.AAS_REPO_COLLECTION); - MongoDBUtilities.clearCollection(mongoTemplate, MongoDbCollectionsTestConfig.SM_REPO_COLLECTION); - MongoDBUtilities.clearCollection(mongoTemplate, MongoDbCollectionsTestConfig.CD_REPO_COLLECTION); - } + @Autowired + private MongoTemplate mongoTemplate; @Test public void aasRepoCollectionIsCorrectlyDefined() { diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml index 45c8dc476..d81ffb8a4 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml +++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml @@ -41,7 +41,6 @@ org.eclipse.digitaltwin.basyx basyx.aasservice-backend-inmemory - test diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackend.java b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackend.java new file mode 100644 index 000000000..b7003eb6f --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackend.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import java.util.function.Consumer; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasRepositoryBackend; +import org.eclipse.digitaltwin.basyx.aasservice.AasService; +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; +import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; +import org.eclipse.digitaltwin.basyx.common.backend.inmemory.core.InMemoryCrudRepository; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; +import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; + +/** + * InMemory implementation of the {@link AasRepositoryBackend} based on the + * {@link AasService} + * + * @author mateusmolina + */ +public class InMemoryAasRepositoryBackend extends InMemoryCrudRepository implements AasRepositoryBackend { + + private final AasServiceFactory aasServiceFactory; + + public InMemoryAasRepositoryBackend(AasServiceFactory aasServiceFactory) { + super(aas -> aas.getId()); + this.aasServiceFactory = aasServiceFactory; + } + + public static InMemoryAasRepositoryBackend buildDefault() { + return new InMemoryAasRepositoryBackend(new InMemoryAasServiceFactory(new InMemoryFileRepository())); + } + + @Override + public CursorResult> getSubmodelReferences(String aasId, PaginationInfo pInfo) { + return getAasService(aasId).getSubmodelReferences(pInfo); + } + + @Override + public void addSubmodelReference(String aasId, Reference submodelReference) { + doWithService(aasId, s -> s.addSubmodelReference(submodelReference)); + } + + @Override + public void removeSubmodelReference(String aasId, String submodelId) { + doWithService(aasId, s -> s.removeSubmodelReference(submodelId)); + } + + @Override + public void setAssetInformation(String aasId, AssetInformation aasInfo) { + doWithService(aasId, s -> s.setAssetInformation(aasInfo)); + } + + @Override + public AssetInformation getAssetInformation(String aasId) { + return getAasService(aasId).getAssetInformation(); + } + + @Override + public File getThumbnail(String aasId) { + return getAasService(aasId).getThumbnail(); + } + + @Override + public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) { + doWithService(aasId, s -> s.setThumbnail(fileName, contentType, inputStream)); + } + + @Override + public void deleteThumbnail(String aasId) { + doWithService(aasId, s -> s.deleteThumbnail()); + } + + private AasService getAasService(String aasId) { + return findById(aasId).map(aasServiceFactory::create).orElseThrow(() -> new ElementDoesNotExistException(aasId)); + } + + private void doWithService(String aasId, Consumer consumer) { + AasService aasService = getAasService(aasId); + consumer.accept(aasService); + save(aasService.getAAS()); + } +} diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackendProvider.java b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackendConfiguration.java similarity index 70% rename from basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackendProvider.java rename to basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackendConfiguration.java index 44860711d..77ab718e7 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackendProvider.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/InMemoryAasRepositoryBackendConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,12 +25,11 @@ package org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory; -import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasBackendProvider; -import org.eclipse.digitaltwin.basyx.common.backend.inmemory.core.InMemoryCrudRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasRepositoryBackend; +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * @@ -39,12 +38,11 @@ * @author mateusmolina, danish */ @ConditionalOnExpression("'${basyx.backend}'.equals('InMemory')") -@Component -public class AasInMemoryBackendProvider implements AasBackendProvider { +@Configuration +public class InMemoryAasRepositoryBackendConfiguration { - @Override - public CrudRepository getCrudRepository() { - return new InMemoryCrudRepository(AssetAdministrationShell::getId); + @Bean + AasRepositoryBackend getAasBackend(AasServiceFactory aasServiceFactory) { + return new InMemoryAasRepositoryBackend(aasServiceFactory); } - } diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java index 5cfb59865..31a00e478 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -26,7 +26,7 @@ package org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; @@ -36,9 +36,8 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultResource; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositorySuite; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasBackendProvider; import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasservice.AasService; import org.eclipse.digitaltwin.basyx.aasservice.DummyAssetAdministrationShellFactory; import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; @@ -55,15 +54,13 @@ */ public class TestInMemoryAasRepository extends AasRepositorySuite { private static final String CONFIGURED_AAS_REPO_NAME = "configured-aas-repo-name"; - - private AasBackendProvider backendProvider = new AasInMemoryBackendProvider(); private static FileRepository fileRepository; @Override protected AasRepository getAasRepository() { fileRepository = new InMemoryFileRepository(); - return new SimpleAasRepositoryFactory(backendProvider, new InMemoryAasServiceFactory(fileRepository)).create(); + return new CrudAasRepositoryFactory(InMemoryAasRepositoryBackend.buildDefault(), "aas-repo").create(); } @Override @@ -87,7 +84,9 @@ protected AasService getAasServiceWithThumbnail() throws IOException { @Test public void getConfiguredInMemoryAasRepositoryName() { - AasRepository repo = new CrudAasRepository(backendProvider, new InMemoryAasServiceFactory(fileRepository), CONFIGURED_AAS_REPO_NAME); + fileRepository = new InMemoryFileRepository(); + InMemoryAasRepositoryBackend backend = new InMemoryAasRepositoryBackend(new InMemoryAasServiceFactory(fileRepository)); + AasRepository repo = new CrudAasRepository(backend, CONFIGURED_AAS_REPO_NAME); assertEquals(CONFIGURED_AAS_REPO_NAME, repo.getName()); } diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/pom.xml b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/pom.xml index 5ebbb4d7e..3eb349e47 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/pom.xml +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/pom.xml @@ -32,7 +32,7 @@ org.eclipse.digitaltwin.basyx - basyx.aasservice-backend-inmemory + basyx.aasservice-backend-mongodb org.eclipse.digitaltwin.basyx @@ -62,5 +62,10 @@ org.eclipse.digitaltwin.basyx basyx.filerepository-backend-mongodb + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBRepositoryConfiguration.java b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBRepositoryConfiguration.java deleted file mode 100644 index 6a6eb6371..000000000 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBRepositoryConfiguration.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * SPDX-License-Identifier: MIT - ******************************************************************************/ - - -package org.eclipse.digitaltwin.basyx.aasrepository.backend.mongodb; - -import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; -import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Provides a InMemoryAasServiceFactory for usage in the MongoDB Aas Repository - * backend.
- *
- * This is needed to ensure that the AasServiceFeatures are processed correctly - * when utilizing MongoDb - * - * @author schnicke - * - */ -@Configuration -@ConditionalOnExpression("'${basyx.backend}'.equals('MongoDB')") -public class AasMongoDBRepositoryConfiguration { - @Bean - public AasServiceFactory getAasServiceFactory(FileRepository fileRepository) { - return new InMemoryAasServiceFactory(fileRepository); - } -} diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBBackendProvider.java b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/MongoDBAasRepositoryBackendConfiguration.java similarity index 60% rename from basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBBackendProvider.java rename to basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/MongoDBAasRepositoryBackendConfiguration.java index 65ca1d6b2..2f6791971 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasMongoDBBackendProvider.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/MongoDBAasRepositoryBackendConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -26,47 +26,44 @@ package org.eclipse.digitaltwin.basyx.aasrepository.backend.mongodb; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasBackendProvider; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceOperations; +import org.eclipse.digitaltwin.basyx.aasservice.backend.MongoDBAasServiceOperations; import org.eclipse.digitaltwin.basyx.common.mongocore.BasyxMongoMappingContext; -import org.springframework.beans.factory.annotation.Autowired; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; -import org.springframework.data.mongodb.repository.support.SimpleMongoRepository; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; /** * - * MongoDB Backend Provider for the AAS + * Provides the MongoDB configuration for the {@link AasRepository} * - * @author mateusmolina, despen + * @author schnicke, mateusmolina + * */ +@Configuration @ConditionalOnExpression("'${basyx.backend}'.equals('MongoDB')") -@Component -public class AasMongoDBBackendProvider implements AasBackendProvider { - - private BasyxMongoMappingContext mappingContext; - - private MongoTemplate template; - - @Autowired - public AasMongoDBBackendProvider(BasyxMongoMappingContext mappingContext, @Value("${basyx.aasrepository.mongodb.collectionName:aas-repo}") String collectionName, MongoTemplate template) { - super(); - this.mappingContext = mappingContext; - this.template = template; - +@EnableMongoRepositories(basePackages = "org.eclipse.digitaltwin.basyx.aasrepository.backend") +public class MongoDBAasRepositoryBackendConfiguration { + @Bean + MappingMongoEntityInformation mappingMongoEntityInformation(BasyxMongoMappingContext mappingContext, @Value("${basyx.aasrepository.mongodb.collectionName:aas-repo}") String collectionName) { mappingContext.addEntityMapping(AssetAdministrationShell.class, collectionName); - } - @Override - public CrudRepository getCrudRepository() { @SuppressWarnings("unchecked") MongoPersistentEntity entity = (MongoPersistentEntity) mappingContext.getPersistentEntity(AssetAdministrationShell.class); - - return new SimpleMongoRepository<>(new MappingMongoEntityInformation<>(entity), template); + return new MappingMongoEntityInformation<>(entity); } + @Bean + @DependsOn("mappingMongoEntityInformation") + AasServiceOperations aasServiceOperations(MongoOperations template, FileRepository fileRepository) { + return new MongoDBAasServiceOperations(template, fileRepository); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/DummyAasRepositoryConfig.java b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/DummyAasRepositoryConfig.java index 07f45487e..41d969777 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/DummyAasRepositoryConfig.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/DummyAasRepositoryConfig.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,17 +25,12 @@ package org.eclipse.digitaltwin.basyx.aasrepository.backend.mongodb; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; -import org.eclipse.digitaltwin.basyx.common.mongocore.BasyxMongoMappingContext; -import org.eclipse.digitaltwin.basyx.core.filerepository.MongoDBFileRepository; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.gridfs.GridFsTemplate; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; /** * Configuration for tests @@ -43,18 +38,13 @@ * @author mateusmolina, danish * */ -@Configuration +@TestConfiguration public class DummyAasRepositoryConfig { - public final static String COLLECTION = "aasRepositoryPersistencyTestCollection"; - public final static String DB = "BaSyxTestDb"; + public static final String COLLECTION = "aasRepositoryPersistencyTestCollection"; + public static final String DB = "BaSyxTestDb"; @Bean - public AasRepository createAasRepository(MongoTemplate template) { - return new SimpleAasRepositoryFactory(new AasMongoDBBackendProvider(new BasyxMongoMappingContext(), COLLECTION, template), new InMemoryAasServiceFactory(new MongoDBFileRepository(configureDefaultGridFsTemplate(template)))).create(); - } - - @Bean - public MongoTemplate createAasRepoMongoTemplate() { + MongoTemplate createAasRepoMongoTemplate() { String connectionURL = "mongodb://mongoAdmin:mongoPassword@localhost:27017/"; MongoClient client = MongoClients.create(connectionURL); @@ -62,8 +52,4 @@ public MongoTemplate createAasRepoMongoTemplate() { return new MongoTemplate(client, DB); } - private GridFsTemplate configureDefaultGridFsTemplate(MongoTemplate mongoTemplate) { - return new GridFsTemplate(mongoTemplate.getMongoDatabaseFactory(), mongoTemplate.getConverter()); - } - } \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/TestMongoDBAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/TestMongoDBAasRepository.java index c854b5652..9ad1f79d8 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/TestMongoDBAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/TestMongoDBAasRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,7 +25,7 @@ package org.eclipse.digitaltwin.basyx.aasrepository.backend.mongodb; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.util.Arrays; @@ -39,23 +39,19 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositorySuite; import org.eclipse.digitaltwin.basyx.aasrepository.DummyAasFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasBackendProvider; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasservice.AasService; import org.eclipse.digitaltwin.basyx.aasservice.DummyAssetAdministrationShellFactory; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; -import org.eclipse.digitaltwin.basyx.common.mongocore.BasyxMongoMappingContext; import org.eclipse.digitaltwin.basyx.common.mongocore.MongoDBUtilities; import org.eclipse.digitaltwin.basyx.core.filerepository.FileMetadata; import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; -import org.eclipse.digitaltwin.basyx.core.filerepository.MongoDBFileRepository; +import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.gridfs.GridFsTemplate; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; /** * Integration Test for MongoDBAasRepository @@ -65,51 +61,57 @@ * @author schnicke, danish, kammognie, mateusmolina, despen * */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@SpringBootTest +@RunWith(SpringRunner.class) public class TestMongoDBAasRepository extends AasRepositorySuite { private static final String COLLECTION = "testAasCollection"; private static final String CONFIGURED_AAS_REPO_NAME = "configured-aas-repo-name"; - - private static MongoTemplate mongoTemplate = createMongoTemplate(); - private static GridFsTemplate gridFsTemplate = configureDefaultGridFsTemplate(mongoTemplate); + @Autowired + private FileRepository fileRepository; + + @Autowired + private AasRepositoryFactory aasRepositoryFactory; + + @Autowired + private MongoTemplate mongoTemplate; - private static FileRepository fileRepository; - @Override protected AasRepository getAasRepository() { - MongoDBUtilities.clearCollection(mongoTemplate, COLLECTION); - fileRepository = new MongoDBFileRepository(gridFsTemplate); - AasBackendProvider aasBackendProvider = new AasMongoDBBackendProvider(new BasyxMongoMappingContext(), COLLECTION, mongoTemplate); - AasRepositoryFactory aasRepositoryFactory = new SimpleAasRepositoryFactory(aasBackendProvider, new InMemoryAasServiceFactory(fileRepository)); - return aasRepositoryFactory.create(); } + @After + public void cleanup() { + MongoDBUtilities.clearCollection(mongoTemplate, COLLECTION); + } + @Override protected AasService getAasServiceWithThumbnail() throws IOException { AssetAdministrationShell expected = DummyAssetAdministrationShellFactory.createForThumbnail(); AasService aasServiceWithThumbnail = getAasService(expected); FileMetadata defaultThumbnail = new FileMetadata("dummyImgA.jpeg", "", createDummyImageIS_A()); - - if(fileRepository.exists(defaultThumbnail.getFileName())) + + if (fileRepository.exists(defaultThumbnail.getFileName())) fileRepository.delete(defaultThumbnail.getFileName()); - + String thumbnailFilePath = fileRepository.save(defaultThumbnail); - + Resource defaultResource = new DefaultResource.Builder().path(thumbnailFilePath).contentType("").build(); AssetInformation defaultAasAssetInformation = aasServiceWithThumbnail.getAssetInformation(); defaultAasAssetInformation.setDefaultThumbnail(defaultResource); - + aasServiceWithThumbnail.setAssetInformation(defaultAasAssetInformation); - + return aasServiceWithThumbnail; } @Test public void aasIsPersisted() { AasRepository aasRepository = getAasRepository(); - + AssetAdministrationShell expectedShell = createDummyShellOnRepo(aasRepository); AssetAdministrationShell retrievedShell = aasRepository.getAas(expectedShell.getId()); @@ -119,22 +121,20 @@ public void aasIsPersisted() { @Test public void updatedAasIsPersisted() { AasRepository aasRepository = getAasRepository(); - + AssetAdministrationShell expectedShell = createDummyShellOnRepo(aasRepository); addSubmodelReferenceToAas(expectedShell); - + aasRepository.updateAas(expectedShell.getId(), expectedShell); AssetAdministrationShell retrievedShell = aasRepository.getAas(expectedShell.getId()); assertEquals(expectedShell, retrievedShell); } - + @Test public void getConfiguredMongoDBAasRepositoryName() { - AasRepository repo = new CrudAasRepository(new AasMongoDBBackendProvider(new BasyxMongoMappingContext(), COLLECTION, mongoTemplate), new InMemoryAasServiceFactory(fileRepository), CONFIGURED_AAS_REPO_NAME); - - assertEquals(CONFIGURED_AAS_REPO_NAME, repo.getName()); + // todo } private void addSubmodelReferenceToAas(AssetAdministrationShell expectedShell) { @@ -147,16 +147,4 @@ private AssetAdministrationShell createDummyShellOnRepo(AasRepository aasReposit aasRepository.createAas(expectedShell); return expectedShell; } - - private static MongoTemplate createMongoTemplate() { - String connectionURL = "mongodb://mongoAdmin:mongoPassword@localhost:27017/"; - - MongoClient client = MongoClients.create(connectionURL); - - return new MongoTemplate(client, "BaSyxTestDb"); - } - - private static GridFsTemplate configureDefaultGridFsTemplate(MongoTemplate mongoTemplate) { - return new GridFsTemplate(mongoTemplate.getMongoDatabaseFactory(), mongoTemplate.getConverter()); - } } diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/application.properties b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/application.properties new file mode 100644 index 000000000..318ad29f9 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/application.properties @@ -0,0 +1,9 @@ +basyx.backend = MongoDB +basyx.aasrepository.mongodb.collectionName= testAasCollection + +spring.data.mongodb.host=127.0.0.1 +spring.data.mongodb.port=27017 +spring.data.mongodb.database=aas-env +spring.data.mongodb.authentication-database=admin +spring.data.mongodb.username=mongoAdmin +spring.data.mongodb.password=mongoPassword \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasBackendProvider.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryBackend.java similarity index 79% rename from basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasBackendProvider.java rename to basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryBackend.java index 2fe0fd33a..eaff7b179 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasBackendProvider.java +++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryBackend.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -22,16 +22,20 @@ * * SPDX-License-Identifier: MIT ******************************************************************************/ + package org.eclipse.digitaltwin.basyx.aasrepository.backend; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceOperations; import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; /** - * Backend provider for the AAS + * Backend interface for the {@link AssetAdministrationShell} * - * @author mateusmolina, despen + * @author mateusmolina */ -public interface AasBackendProvider { - public CrudRepository getCrudRepository(); -} +@Repository +public interface AasRepositoryBackend extends CrudRepository, AasServiceOperations { + +} \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryFragmentConfiguration.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryFragmentConfiguration.java new file mode 100644 index 000000000..67277cd2a --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AasRepositoryFragmentConfiguration.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.backend; + +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceOperations; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.data.repository.core.support.RepositoryFragment; +import org.springframework.lang.NonNull; + +/** + * Configures the Spring Data repository fragments for the + * {@link AasRepositoryBackend} + * + * Requires a bean with the name "aasServiceOperations". It is composed with the + * {@link AasRepositoryBackend} as a {@link RepositoryFragment} + * + * @author mateusmolina + */ +@Configuration +@ConditionalOnBean(name="aasServiceOperations") +public class AasRepositoryFragmentConfiguration implements BeanPostProcessor { + + private final AasServiceOperations aasServiceOperations; + + public AasRepositoryFragmentConfiguration(@Qualifier("aasServiceOperations") AasServiceOperations aasServiceOperations) { + this.aasServiceOperations = aasServiceOperations; + } + + @Override + public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { + if (!(bean instanceof RepositoryFactoryBeanSupport beanfactory)) + return bean; + + if (!beanfactory.getObjectType().equals(AasRepositoryBackend.class)) + return bean; + + RepositoryFragment fragment = RepositoryFragment.implemented(AasServiceOperations.class, aasServiceOperations); + RepositoryFragments fragments = RepositoryFragments.of(fragment); + beanfactory.setRepositoryFragments(fragments); + + return bean; + } + +} \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java index f78c7e045..8e03ee767 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -35,8 +35,6 @@ import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasservice.AasService; -import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.core.exceptions.IdentificationMismatchException; @@ -56,20 +54,12 @@ */ public class CrudAasRepository implements AasRepository { - private CrudRepository aasBackend; + private final AasRepositoryBackend aasBackend; - private AasServiceFactory aasServiceFactory; - - private String aasRepositoryName = null; - - public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory) { - this.aasBackend = aasBackendProvider.getCrudRepository(); - this.aasServiceFactory = aasServiceFactory; - } - - public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory, @Value("${basyx.aasrepo.name:aas-repo}") String aasRepositoryName) { - this(aasBackendProvider, aasServiceFactory); + private final String aasRepositoryName; + public CrudAasRepository(AasRepositoryBackend aasBackend, @Value("${basyx.aasrepo.name:aas-repo}") String aasRepositoryName) { + this.aasBackend = aasBackend; this.aasRepositoryName = aasRepositoryName; } @@ -77,7 +67,7 @@ public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactor public CursorResult> getAllAas(PaginationInfo pInfo) { Iterable iterable = aasBackend.findAll(); - List allAas = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); + List allAas = StreamSupport.stream(iterable.spliterator(), false).toList(); TreeMap aasMap = allAas.stream().collect(Collectors.toMap(AssetAdministrationShell::getId, aas -> aas, (a, b) -> a, TreeMap::new)); @@ -94,7 +84,7 @@ public AssetAdministrationShell getAas(String aasId) throws ElementDoesNotExistE @Override public void createAas(AssetAdministrationShell aas) throws CollidingIdentifierException, MissingIdentifierException { throwIfAasIdEmptyOrNull(aas.getId()); - + throwIfAasExists(aas); aasBackend.save(aas); @@ -118,42 +108,29 @@ public void updateAas(String aasId, AssetAdministrationShell aas) { @Override public CursorResult> getSubmodelReferences(String aasId, PaginationInfo pInfo) { - return getAasServiceOrThrow(aasId).getSubmodelReferences(pInfo); + return aasBackend.getSubmodelReferences(aasId, pInfo); } @Override public void addSubmodelReference(String aasId, Reference submodelReference) { - AasService aasService = getAasServiceOrThrow(aasId); - - aasService.addSubmodelReference(submodelReference); - - updateAas(aasId, aasService.getAAS()); + aasBackend.addSubmodelReference(aasId, submodelReference); } @Override public void removeSubmodelReference(String aasId, String submodelId) { - AasService aasService = getAasServiceOrThrow(aasId); - - aasService.removeSubmodelReference(submodelId); - - updateAas(aasId, aasService.getAAS()); + aasBackend.removeSubmodelReference(aasId, submodelId); } @Override public void setAssetInformation(String aasId, AssetInformation aasInfo) throws ElementDoesNotExistException { - AasService aasService = getAasServiceOrThrow(aasId); - - aasService.setAssetInformation(aasInfo); - - updateAas(aasId, aasService.getAAS()); + aasBackend.setAssetInformation(aasId, aasInfo); } @Override public AssetInformation getAssetInformation(String aasId) throws ElementDoesNotExistException { - return getAasServiceOrThrow(aasId).getAssetInformation(); + return aasBackend.getAssetInformation(aasId); } - @Override public String getName() { return aasRepositoryName == null ? AasRepository.super.getName() : aasRepositoryName; @@ -161,31 +138,17 @@ public String getName() { @Override public File getThumbnail(String aasId) { - return getAasServiceOrThrow(aasId).getThumbnail(); + return aasBackend.getThumbnail(aasId); } @Override public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) { - AasService aasService = getAasServiceOrThrow(aasId); - - aasService.setThumbnail(fileName, contentType, inputStream); - - updateAas(aasId, aasService.getAAS()); + aasBackend.setThumbnail(aasId, fileName, contentType, inputStream); } @Override public void deleteThumbnail(String aasId) { - AasService aasService = getAasServiceOrThrow(aasId); - - aasService.deleteThumbnail(); - - updateAas(aasId, aasService.getAAS()); - } - - private AasService getAasServiceOrThrow(String aasId) { - AssetAdministrationShell aas = aasBackend.findById(aasId).orElseThrow(() -> new ElementDoesNotExistException(aasId)); - - return aasServiceFactory.create(aas); + aasBackend.deleteThumbnail(aasId); } private void throwIfMismatchingIds(String aasId, AssetAdministrationShell newAas) { @@ -200,9 +163,9 @@ private void throwIfAasExists(AssetAdministrationShell aas) { if (aasBackend.existsById(aasId)) throw new CollidingIdentifierException(aasId); } - + private void throwIfAasIdEmptyOrNull(String aasId) { - if(aasId == null || aasId.isBlank()) + if (aasId == null || aasId.isBlank()) throw new MissingIdentifierException(aasId); } diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/SimpleAasRepositoryFactory.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryFactory.java similarity index 62% rename from basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/SimpleAasRepositoryFactory.java rename to basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryFactory.java index c9bbdb3c3..cb92e9850 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/SimpleAasRepositoryFactory.java +++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -27,44 +27,30 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; /** - * Simple AAS repository factory that creates a {@link CrudAasRepository} with - * a backend provider and a service factory + * Simple AAS repository factory that provides the {@link CrudAasRepository} + * component * * @author mateusmolina * */ @Component @ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.backend:}')") -public class SimpleAasRepositoryFactory implements AasRepositoryFactory { +public class CrudAasRepositoryFactory implements AasRepositoryFactory { - private AasBackendProvider aasBackendProvider; + private final CrudAasRepository crudAasRepository; - private AasServiceFactory aasServiceFactory; - - private String aasRepositoryName = null; - - @Autowired(required = false) - public SimpleAasRepositoryFactory(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory) { - this.aasBackendProvider = aasBackendProvider; - this.aasServiceFactory = aasServiceFactory; - } - - @Autowired(required = false) - public SimpleAasRepositoryFactory(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory, @Value("${basyx.aasrepo.name:aas-repo}") String aasRepositoryName) { - this(aasBackendProvider, aasServiceFactory); - this.aasRepositoryName = aasRepositoryName; + public CrudAasRepositoryFactory(AasRepositoryBackend aasRepositoryBackend, @Value("${basyx.aasrepo.name:aas-repo}") String aasRepositoryName) { + this.crudAasRepository = new CrudAasRepository(aasRepositoryBackend, aasRepositoryName); } @Override public AasRepository create() { - return new CrudAasRepository(aasBackendProvider, aasServiceFactory, aasRepositoryName); + return crudAasRepository; } } diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryTest.java b/basyx.aasrepository/basyx.aasrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryTest.java index 3b177cf2a..207b62b42 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryTest.java +++ b/basyx.aasrepository/basyx.aasrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepositoryTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,10 +24,9 @@ ******************************************************************************/ package org.eclipse.digitaltwin.basyx.aasrepository.backend; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository; import org.junit.Test; /** @@ -41,7 +40,7 @@ public class CrudAasRepositoryTest { @Test public void getConfiguredAasRepositoryName() { - AasRepository repo = new CrudAasRepository(() -> null, aas -> null, CONFIGURED_AAS_REPO_NAME); + AasRepository repo = new CrudAasRepository(null, CONFIGURED_AAS_REPO_NAME); assertEquals(CONFIGURED_AAS_REPO_NAME, repo.getName()); } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/TestMqttV2AASAggregatorObserver.java b/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/TestMqttV2AASAggregatorObserver.java index fd61cf76b..25d89d2ad 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/TestMqttV2AASAggregatorObserver.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/TestMqttV2AASAggregatorObserver.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,7 +24,7 @@ ******************************************************************************/ package org.eclipse.digitaltwin.basyx.aasrepository.feature.mqtt; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; @@ -39,13 +39,11 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasrepository.DummyAasFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.InMemoryAasRepositoryBackend; import org.eclipse.digitaltwin.basyx.common.mqttcore.encoding.Base64URLEncoder; import org.eclipse.digitaltwin.basyx.common.mqttcore.encoding.URLEncoder; import org.eclipse.digitaltwin.basyx.common.mqttcore.listener.MqttTestListener; -import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttSecurityException; @@ -170,7 +168,7 @@ private AssetAdministrationShell createAasWithSubmodelReference(String aasId) { } private static AasRepository createMqttAasRepository(MqttClient client) { - AasRepositoryFactory repoFactory = new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository())); + AasRepositoryFactory repoFactory = new CrudAasRepositoryFactory(InMemoryAasRepositoryBackend.buildDefault(), "aas-repo"); return new MqttAasRepositoryFactory(repoFactory, client, new MqttAasRepositoryTopicFactory(new URLEncoder())).create(); } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java index 8b3739033..367a22402 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,7 +25,7 @@ package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.Arrays; import java.util.Collection; @@ -38,10 +38,8 @@ import org.eclipse.digitaltwin.basyx.aasregistry.main.client.factory.AasDescriptorFactory; import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.AttributeMapper; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; -import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.InMemoryAasRepositoryBackend; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.junit.Before; import org.junit.Test; @@ -115,7 +113,7 @@ private AssetAdministrationShell createDummyAas() { } protected AasRepository getAasRepository() { - return new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository())).create(); + return new CrudAasRepositoryFactory(InMemoryAasRepositoryBackend.buildDefault(), "aas-repo").create(); } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java index e35a8130a..7b7c81152 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -31,7 +31,7 @@ import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.springframework.boot.SpringApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; /** @@ -48,7 +48,7 @@ public class AasRepositoryRegistryLinkTest extends AasRepositoryRegistryLinkTest @BeforeClass public static void setUp() throws FileNotFoundException, IOException { - appContext = new SpringApplication(DummyAasRepositoryIntegrationComponent.class).run(new String[] {}); + appContext = new SpringApplicationBuilder(DummyAasRepositoryIntegrationComponent.class).profiles("regintegration").run(new String[] {}); aasRepositoryRegistryLink = appContext.getBean(AasRepositoryRegistryLink.class); } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java index cb8a61601..d95ab87ea 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,13 +25,10 @@ package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.FileNotFoundException; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.util.List; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; @@ -43,8 +40,6 @@ import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpStatus; diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java index c56d5db09..d8f6b36f0 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -58,7 +58,7 @@ public class AuthorizedAasRepositoryRegistryLinkTest extends AasRepositoryRegist @BeforeClass public static void setUp() throws FileNotFoundException, IOException { SpringApplication application = new SpringApplication(DummyAasRepositoryIntegrationComponent.class); - application.setAdditionalProfiles("authregistry"); + application.setAdditionalProfiles("authregistry", "regintegration"); appContext = application.run(new String[] {}); diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/MultiUrlAasRepositoryRegistryLinkTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/MultiUrlAasRepositoryRegistryLinkTest.java index df660d5cb..e8f944728 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/MultiUrlAasRepositoryRegistryLinkTest.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/MultiUrlAasRepositoryRegistryLinkTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -49,7 +49,7 @@ public class MultiUrlAasRepositoryRegistryLinkTest extends AasRepositoryRegistry @BeforeClass public static void setUp() throws FileNotFoundException, IOException { SpringApplication application = new SpringApplication(DummyAasRepositoryIntegrationComponent.class); - application.setAdditionalProfiles("multiurl"); + application.setAdditionalProfiles("multiurl", "regintegration"); appContext = application.run(new String[] {}); diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestConfiguration.java similarity index 78% rename from basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java rename to basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestConfiguration.java index 94a3e7d6d..d191305a6 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -31,23 +31,15 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasrepository.feature.AasRepositoryFeature; import org.eclipse.digitaltwin.basyx.aasrepository.feature.DecoratedAasRepositoryFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; - -/** - * Configuration for tests - * - * @author danish - * - */ @Configuration -public class DummyAasRepositoryIntegrationConfiguration { - - @Bean - @ConditionalOnMissingBean - public static AasRepository getAasRepository(AasRepositoryFactory aasRepositoryFactory, List features) { - return new DecoratedAasRepositoryFactory(aasRepositoryFactory, features).create(); - } +@Profile("regintegration") +public class RegistryIntegrationTestConfiguration { + @Bean + AasRepository getAasRepository(AasRepositoryFactory aasRepositoryFactory, List features) { + return new DecoratedAasRepositoryFactory(aasRepositoryFactory, features).create(); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/testconfig/DummyAasRepositoryConfig.java b/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/testconfig/DummyAasRepositoryConfig.java index e46cdd789..2f95a7f62 100644 --- a/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/testconfig/DummyAasRepositoryConfig.java +++ b/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/testconfig/DummyAasRepositoryConfig.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -26,11 +26,8 @@ package org.eclipse.digitaltwin.basyx.aasrepository.http.testconfig; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; -import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasRepositoryBackend; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -39,16 +36,14 @@ /** * Configuration for tests * - * @author danish, kammognie + * @author danish, kammognie, mateusmolina * */ @Configuration @Profile("httptests") public class DummyAasRepositoryConfig { - - @Bean - @ConditionalOnMissingBean - public AasRepository createAasRepository() { - return new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository())).create(); - } + @Bean + AasRepository createAasRepository(AasRepositoryBackend backend) { + return new CrudAasRepositoryFactory(backend, "aas-repo").create(); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/application-httptests.properties b/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/application-httptests.properties new file mode 100644 index 000000000..bdb9bb89c --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/application-httptests.properties @@ -0,0 +1,3 @@ +server.port=8080 + +basyx.backend = InMemory \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AasRepositoryConfiguration.java b/basyx.aasrepository/basyx.aasrepository.component/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AasRepositoryConfiguration.java index cd5c065b3..8f0489d4c 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AasRepositoryConfiguration.java +++ b/basyx.aasrepository/basyx.aasrepository.component/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AasRepositoryConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,19 +32,15 @@ import org.eclipse.digitaltwin.basyx.aasrepository.feature.AasRepositoryFeature; import org.eclipse.digitaltwin.basyx.aasrepository.feature.DecoratedAasRepositoryFactory; import org.eclipse.digitaltwin.basyx.aasservice.AasService; -import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; -import org.eclipse.digitaltwin.basyx.aasservice.feature.AasServiceFeature; -import org.eclipse.digitaltwin.basyx.aasservice.feature.DecoratedAasServiceFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; /** * Provides the spring bean configuration for the {@link AasRepository} and * {@link AasService} utilizing all found features for the respective services * - * @author schnicke + * @author schnicke, mateusmolina * */ @Configuration @@ -54,10 +50,4 @@ public class AasRepositoryConfiguration { public static AasRepository getAasRepository(AasRepositoryFactory aasRepositoryFactory, List features) { return new DecoratedAasRepositoryFactory(aasRepositoryFactory, features).create(); } - - @Primary - @Bean - public static AasServiceFactory getAasService(AasServiceFactory aasServiceFactory, List features) { - return new DecoratedAasServiceFactory(aasServiceFactory, features); - } } diff --git a/basyx.aasservice/basyx.aasservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/InMemoryAasService.java b/basyx.aasservice/basyx.aasservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/InMemoryAasService.java index 0fc9b9516..85bf16d68 100644 --- a/basyx.aasservice/basyx.aasservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/InMemoryAasService.java +++ b/basyx.aasservice/basyx.aasservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/InMemoryAasService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,10 +25,7 @@ package org.eclipse.digitaltwin.basyx.aasservice.backend; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.List; import java.util.Optional; import java.util.TreeMap; @@ -43,9 +40,12 @@ import org.eclipse.digitaltwin.aas4j.v3.model.Resource; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultResource; import org.eclipse.digitaltwin.basyx.aasservice.AasService; -import org.eclipse.digitaltwin.basyx.core.exceptions.*; -import org.eclipse.digitaltwin.basyx.core.filerepository.FileMetadata; +import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingSubmodelReferenceException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.MissingKeyTypeException; import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepositoryHelper; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; @@ -150,50 +150,24 @@ private Function extractSubmodelID() { @Override public File getThumbnail() { - Resource resource = getAssetInformation().getDefaultThumbnail(); - - try { - return getResourceContent(resource); - } catch (NullPointerException e) { - throw new FileDoesNotExistException(); - } catch (IOException e) { - throw new FileHandlingException("Exception occurred while creating file from the InputStream." + e.getMessage()); - } + return FileRepositoryHelper.fetchAndStoreFileLocally(fileRepository, getThumbnailResourcePathOrThrow(getAssetInformation())); } @Override public void setThumbnail(String fileName, String contentType, InputStream inputStream) { - FileMetadata thumbnailMetadata = new FileMetadata(fileName, contentType, inputStream); - - if(fileRepository.exists(thumbnailMetadata.getFileName())) - fileRepository.delete(thumbnailMetadata.getFileName()); - - String filePath = fileRepository.save(thumbnailMetadata); - + String filePath = FileRepositoryHelper.saveOrOverwriteFile(fileRepository, fileName, contentType, inputStream); setAssetInformation(configureAssetInformationThumbnail(getAssetInformation(), contentType, filePath)); } @Override public void deleteThumbnail() { - try { - String thumbnailPath = getAssetInformation().getDefaultThumbnail().getPath(); - fileRepository.delete(thumbnailPath); - } catch (NullPointerException e) { - throw new FileDoesNotExistException(); - } finally { - setAssetInformation(configureAssetInformationThumbnail(getAssetInformation(), "", "")); - } - + AssetInformation assetInformation = getAssetInformation(); + FileRepositoryHelper.removeFileIfExists(fileRepository, getThumbnailResourcePathOrThrow(assetInformation)); + setAssetInformation(configureAssetInformationThumbnail(assetInformation, "", "")); } - private void createOutputStream(String filePath, byte[] content) throws IOException { - - try (OutputStream outputStream = new FileOutputStream(filePath)) { - outputStream.write(content); - } catch (IOException e) { - throw new FileHandlingException("Exception occurred while creating OutputStream from byte[]." + e.getMessage()); - } - + private String getThumbnailResourcePathOrThrow(AssetInformation assetInformation) { + return Optional.ofNullable(assetInformation).map(AssetInformation::getDefaultThumbnail).map(Resource::getPath).orElseThrow(FileDoesNotExistException::new); } private static AssetInformation configureAssetInformationThumbnail(AssetInformation assetInformation, String contentType, String filePath) { @@ -204,18 +178,6 @@ private static AssetInformation configureAssetInformationThumbnail(AssetInformat return assetInformation; } - private File getResourceContent(Resource resource) throws IOException { - String filePath = resource.getPath(); - - InputStream fileIs = fileRepository.find(filePath); - byte[] content = fileIs.readAllBytes(); - fileIs.close(); - - createOutputStream(filePath, content); - - return new java.io.File(filePath); - } - private void throwExceptionIfReferenceIsAlreadyPresent(Reference submodelReference) { Optional submodelIdKey = getSubmodelTypeKey(submodelReference); if(submodelIdKey.isEmpty()) diff --git a/basyx.aasservice/basyx.aasservice-backend-mongodb/pom.xml b/basyx.aasservice/basyx.aasservice-backend-mongodb/pom.xml new file mode 100644 index 000000000..3190d1b23 --- /dev/null +++ b/basyx.aasservice/basyx.aasservice-backend-mongodb/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + + org.eclipse.digitaltwin.basyx + basyx.aasservice + ${revision} + + + basyx.aasservice-backend-mongodb + BaSyx AAS Service backend-mongodb + BaSyx AAS Service backend-mongodb + + + + org.eclipse.digitaltwin.basyx + basyx.aasservice-core + + + org.eclipse.digitaltwin.basyx + basyx.aasservice-core + tests + test + + + + org.springframework.boot + spring-boot-starter + + + org.eclipse.digitaltwin.basyx + basyx.filerepository-backend-mongodb + + + \ No newline at end of file diff --git a/basyx.aasservice/basyx.aasservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/MongoDBAasServiceOperations.java b/basyx.aasservice/basyx.aasservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/MongoDBAasServiceOperations.java new file mode 100644 index 000000000..305ce1c0a --- /dev/null +++ b/basyx.aasservice/basyx.aasservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/backend/MongoDBAasServiceOperations.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasservice.backend; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Key; +import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.Resource; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultResource; +import org.eclipse.digitaltwin.basyx.aasservice.AasServiceOperations; +import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingSubmodelReferenceException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepositoryHelper; +import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.AggregationOperation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; +import org.springframework.data.mongodb.core.aggregation.LimitOperation; +import org.springframework.data.mongodb.core.aggregation.MatchOperation; +import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; +import org.springframework.data.mongodb.core.aggregation.UnwindOperation; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.lang.NonNull; + +import com.mongodb.client.result.UpdateResult; + +/** + * MongoDB implementation of the {@link AasServiceOperations} + * + * @author mateusmolina + */ +public class MongoDBAasServiceOperations implements AasServiceOperations { + + private static final String SMREF_KEY = "submodels"; + private static final String ASSETINFORMATION_KEY = "assetInformation"; + + private final MongoOperations mongoOperations; + private final String collectionName; + private final FileRepository fileRepository; + + public MongoDBAasServiceOperations(MongoOperations mongoOperations, FileRepository fileRepository) { + this.mongoOperations = mongoOperations; + this.fileRepository = fileRepository; + collectionName = mongoOperations.getCollectionName(AssetAdministrationShell.class); + } + + + @Override + public CursorResult> getSubmodelReferences(@NonNull String aasId, @NonNull PaginationInfo pInfo) throws ElementDoesNotExistException { + MatchOperation matchAasId = Aggregation.match(Criteria.where("_id").is(aasId)); + UnwindOperation unwindSubmodels = Aggregation.unwind(SMREF_KEY); + ProjectionOperation projectReference = Aggregation.project().and("submodels.keys").as("keys").and("submodels.type").as("type"); + + List ops = new ArrayList<>(); + ops.add(matchAasId); + ops.add(unwindSubmodels); + ops.add(projectReference); + + String cursor = pInfo.getCursor(); + if (cursor != null && !cursor.isEmpty()) { + ops.add(Aggregation.match(Criteria.where("keys.value").gt(cursor))); + } + if (pInfo.getLimit() != null && pInfo.getLimit() > 0) { + ops.add(new LimitOperation(pInfo.getLimit())); + } + + Aggregation aggregation = Aggregation.newAggregation(ops); + AggregationResults results = mongoOperations.aggregate(aggregation, collectionName, DefaultReference.class); + List refs = results.getMappedResults(); + + if (refs.isEmpty() && !existsAas(aasId)) + throw new ElementDoesNotExistException(aasId); + + String nextCursor = null; + if (!refs.isEmpty()) { + Reference last = refs.get(refs.size() - 1); + nextCursor = extractSubmodelId(last); + } + + return new CursorResult<>(nextCursor, new ArrayList<>(refs)); + } + + @Override + public void addSubmodelReference(@NonNull String aasId, @NonNull Reference submodelReference) throws ElementDoesNotExistException, CollidingSubmodelReferenceException { + String newKeyValue = submodelReference.getKeys().get(0).getValue(); + Query query = new Query(new Criteria().andOperator(Criteria.where("_id").is(aasId), Criteria.where(SMREF_KEY).not().elemMatch(Criteria.where("keys.0.value").is(newKeyValue)))); + Update update = new Update().push(SMREF_KEY, submodelReference); + UpdateResult result = mongoOperations.updateFirst(query, update, collectionName); + + if (result.getMatchedCount() != 0) + return; + + if (!existsAas(aasId)) + throw new ElementDoesNotExistException(aasId); + + throw new CollidingSubmodelReferenceException(newKeyValue); + } + + @Override + public void removeSubmodelReference(@NonNull String aasId, @NonNull String submodelId) throws ElementDoesNotExistException { + Query query = new Query(Criteria.where("_id").is(aasId)); + Update update = new Update().pull(SMREF_KEY, Query.query(Criteria.where("keys.value").is(submodelId)).getQueryObject()); + UpdateResult result = mongoOperations.updateFirst(query, update, collectionName); + + if (result.getModifiedCount() != 0) + return; + + if (!existsAas(aasId)) + throw new ElementDoesNotExistException(aasId); + + throw new ElementDoesNotExistException(submodelId); + } + + @Override + public void setAssetInformation(@NonNull String aasId, @NonNull AssetInformation aasInfo) { + Query query = new Query(Criteria.where("_id").is(aasId)); + + Update update = new Update().set(ASSETINFORMATION_KEY, aasInfo); + + UpdateResult result = mongoOperations.updateFirst(query, update, collectionName); + + // Second check for the case where the update was not performed because the + // aasInfo is the + // same as the existing one + if (result.getModifiedCount() == 0 && !existsAas(aasId)) + throw new ElementDoesNotExistException(aasId); + } + + @Override + public AssetInformation getAssetInformation(@NonNull String aasId) { + Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(Criteria.where("_id").is(aasId)), Aggregation.replaceRoot().withValueOf("$" + ASSETINFORMATION_KEY)); + + AggregationResults results = mongoOperations.aggregate(aggregation, collectionName, DefaultAssetInformation.class // Use concrete type + ); + + DefaultAssetInformation aasInfo = results.getUniqueMappedResult(); + + if (aasInfo == null) + throw new ElementDoesNotExistException(aasId); + + return aasInfo; + } + + @Override + public File getThumbnail(@NonNull String aasId) { + return FileRepositoryHelper.fetchAndStoreFileLocally(fileRepository, getThumbnailResourcePathOrThrow(getAssetInformation(aasId))); + } + + @Override + public void setThumbnail(@NonNull String aasId, @NonNull String fileName, @NonNull String contentType, @NonNull InputStream inputStream) { + String filePath = FileRepositoryHelper.saveOrOverwriteFile(fileRepository, fileName, contentType, inputStream); + setAssetInformation(aasId, configureAssetInformationThumbnail(getAssetInformation(aasId), contentType, filePath)); + } + + @Override + public void deleteThumbnail(@NonNull String aasId) { + AssetInformation assetInformation = getAssetInformation(aasId); + FileRepositoryHelper.removeFileIfExists(fileRepository, getThumbnailResourcePathOrThrow(assetInformation)); + setAssetInformation(aasId, configureAssetInformationThumbnail(assetInformation, "", "")); + } + + private boolean existsAas(String aasId) { + return mongoOperations.exists(new Query(Criteria.where("_id").is(aasId)), AssetAdministrationShell.class, collectionName); + } + + private String getThumbnailResourcePathOrThrow(AssetInformation assetInformation) { + return Optional.ofNullable(assetInformation).map(AssetInformation::getDefaultThumbnail).map(Resource::getPath).orElseThrow(FileDoesNotExistException::new); + } + private static AssetInformation configureAssetInformationThumbnail(AssetInformation assetInformation, String contentType, String filePath) { + Resource resource = new DefaultResource(); + resource.setContentType(contentType); + resource.setPath(filePath); + assetInformation.setDefaultThumbnail(resource); + return assetInformation; + } + + private static String extractSubmodelId(Reference reference) { + List keys = reference.getKeys(); + + for (Key key : keys) { + if (key.getType() == KeyTypes.SUBMODEL) { + return key.getValue(); + } + } + + return ""; + } +} diff --git a/basyx.aasservice/basyx.aasservice-core/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/AasServiceOperations.java b/basyx.aasservice/basyx.aasservice-core/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/AasServiceOperations.java new file mode 100644 index 000000000..99026d86e --- /dev/null +++ b/basyx.aasservice/basyx.aasservice-core/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/AasServiceOperations.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ +package org.eclipse.digitaltwin.basyx.aasservice; + +import java.io.File; +import java.io.InputStream; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingSubmodelReferenceException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.springframework.lang.NonNull; + +/** + * This interface provides backend-level operations for managing + * {@link AssetAdministrationShell}s + * + * @author mateusmolina + * + */ +public interface AasServiceOperations { + + /** + * Retrieves all Submodel References for the given AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @param pInfo + * the pagination information; must not be {@code null} + * @return a {@code CursorResult} containing a list of Submodel References + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + CursorResult> getSubmodelReferences(@NonNull String aasId, @NonNull PaginationInfo pInfo) throws ElementDoesNotExistException; + + /** + * Adds a Submodel Reference to the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @param submodelReference + * the reference to be added; must not be {@code null} + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + * @throws CollidingSubmodelReferenceException + * if the provided submodel reference conflicts with an existing + * reference + */ + void addSubmodelReference(@NonNull String aasId, @NonNull Reference submodelReference) throws ElementDoesNotExistException, CollidingSubmodelReferenceException; + + /** + * Removes a Submodel Reference from the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @param submodelId + * the identifier of the submodel to remove; must not be {@code null} + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + void removeSubmodelReference(@NonNull String aasId, @NonNull String submodelId) throws ElementDoesNotExistException; + + /** + * Sets the asset information of the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @param assetInformation + * the asset information to be set; must not be {@code null} + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + void setAssetInformation(@NonNull String aasId, @NonNull AssetInformation assetInformation) throws ElementDoesNotExistException; + + /** + * Retrieves the asset information of the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @return the asset information of the AAS, or {@code null} if not found + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + AssetInformation getAssetInformation(@NonNull String aasId) throws ElementDoesNotExistException; + + /** + * Retrieves the thumbnail for the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @return the file of the thumbnail, or {@code null} if no thumbnail exists + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + File getThumbnail(@NonNull String aasId) throws ElementDoesNotExistException; + + /** + * Sets the thumbnail for the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @param fileName + * the name of the thumbnail file (including extension); must not be + * {@code null} + * @param contentType + * the MIME type of the file; must not be {@code null} + * @param inputStream + * the input stream containing the thumbnail data; must not be + * {@code null} + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + void setThumbnail(@NonNull String aasId, @NonNull String fileName, @NonNull String contentType, @NonNull InputStream inputStream) throws ElementDoesNotExistException; + + /** + * Deletes the thumbnail of the specified AAS. + * + * @param aasId + * the identifier of the Asset Administration Shell; must not be + * {@code null} + * @throws ElementDoesNotExistException + * if the Asset Administration Shell with the specified + * {@code aasId} does not exist + */ + void deleteThumbnail(@NonNull String aasId) throws ElementDoesNotExistException; +} \ No newline at end of file diff --git a/basyx.aasservice/basyx.aasservice-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/feature/mqtt/TestMqttAasService.java b/basyx.aasservice/basyx.aasservice-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/feature/mqtt/TestMqttAasService.java index d4129dca9..4848b0c1b 100644 --- a/basyx.aasservice/basyx.aasservice-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/feature/mqtt/TestMqttAasService.java +++ b/basyx.aasservice/basyx.aasservice-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/feature/mqtt/TestMqttAasService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -25,7 +25,7 @@ package org.eclipse.digitaltwin.basyx.aasservice.feature.mqtt; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.util.Arrays; @@ -41,8 +41,9 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultResource; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasRepositoryBackend; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.InMemoryAasRepositoryBackend; import org.eclipse.digitaltwin.basyx.aasservice.AasService; import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory; import org.eclipse.digitaltwin.basyx.aasservice.AasServiceSuite; @@ -197,7 +198,8 @@ private static ObjectMapper configureObjectMapper() { } private static AasRepository createMqttAasRepository() { - AasRepositoryFactory repoFactory = new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), mqttAasServiceFactory); + AasRepositoryBackend aasBackend = new InMemoryAasRepositoryBackend(mqttAasServiceFactory); + AasRepositoryFactory repoFactory = new CrudAasRepositoryFactory(aasBackend, "aas-repo"); return repoFactory.create(); } diff --git a/basyx.aasservice/pom.xml b/basyx.aasservice/pom.xml index 68ae18f9d..00216851a 100644 --- a/basyx.aasservice/pom.xml +++ b/basyx.aasservice/pom.xml @@ -16,6 +16,7 @@ basyx.aasservice-core basyx.aasservice-backend-inmemory + basyx.aasservice-backend-mongodb basyx.aasservice-feature-mqtt basyx.aasservice-client diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileHandlingException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileHandlingException.java index 8d7b9c11b..9e5fb8bee 100644 --- a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileHandlingException.java +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileHandlingException.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2025 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -42,6 +42,10 @@ public FileHandlingException(String fileName) { super(getMessage(fileName)); } + public FileHandlingException(String message, Throwable cause) { + super(message, cause); + } + private static String getMessage(String fileName) { return "Exception occurred while handling the file: " + fileName; } diff --git a/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepositoryHelper.java b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepositoryHelper.java new file mode 100644 index 000000000..6ba52154d --- /dev/null +++ b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepositoryHelper.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (C) 2025 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.core.filerepository; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException; + +/** + * Helper class for working with files in services + * + * @author mateusmolina + */ +public class FileRepositoryHelper { + + private FileRepositoryHelper() { + } + + /** + * Retrieves the content of a file from the repository and creates a local file. + * + * @param fileRepository + * the file repository instance + * @param filePath + * @return the retrieved file + * @throws FileHandlingException + * if an IO error occurs + */ + public static File fetchAndStoreFileLocally(FileRepository fileRepository, String filePath) { + try (InputStream fileIs = fileRepository.find(filePath)) { + byte[] content = fileIs.readAllBytes(); + writeToFile(filePath, content); + return new File(filePath); + } catch (IOException e) { + throw new FileHandlingException("Error while retrieving file content:" + filePath, e); + } + } + + /** + * Writes byte content to a file on disk. + * + * @param filePath + * the path where the file should be created + * @param content + * the byte content to be written to the file + * @throws FileHandlingException + * if an IO error occurs + */ + public static void writeToFile(String filePath, byte[] content) { + try (OutputStream outputStream = new FileOutputStream(filePath)) { + outputStream.write(content); + } catch (IOException e) { + throw new FileHandlingException("Error while writing to file: " + filePath, e); + } + } + + /** + * Saves a file to the repository, deleting any existing file with the same + * name. + * + * @param fileRepository + * the file repository instance + * @param fileName + * the name of the file + * @param contentType + * the MIME type of the file + * @param inputStream + * the input stream containing file data + * @return the path where the file is saved + */ + public static String saveOrOverwriteFile(FileRepository fileRepository, String fileName, String contentType, InputStream inputStream) { + FileMetadata fileMetadata = new FileMetadata(fileName, contentType, inputStream); + + if (fileRepository.exists(fileName)) { + fileRepository.delete(fileName); + } + return fileRepository.save(fileMetadata); + } + + /** + * Deletes a file from the repository. + * + * @param fileRepository + * the file repository instance + * @param filePath + * the path of the file to be deleted + * @throws FileDoesNotExistException + * if the file does not exist + */ + public static void removeFileIfExists(FileRepository fileRepository, String filePath) { + if (fileRepository.exists(filePath)) { + fileRepository.delete(filePath); + } else { + throw new FileDoesNotExistException(); + } + } +} diff --git a/pom.xml b/pom.xml index 90911b21b..90ca37e10 100644 --- a/pom.xml +++ b/pom.xml @@ -562,6 +562,11 @@ basyx.aasservice-backend-inmemory ${revision} + + org.eclipse.digitaltwin.basyx + basyx.aasservice-backend-mongodb + ${revision} + org.eclipse.digitaltwin.basyx basyx.aasservice-feature-mqtt @@ -946,6 +951,12 @@ ${revision} tests + + org.eclipse.digitaltwin.basyx + basyx.aasservice-backend-mongodb + ${revision} + tests + org.eclipse.digitaltwin.basyx basyx.aasservice-feature-mqtt