diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/pom.xml b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/pom.xml
index f20e475d9..4fca3236b 100644
--- a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/pom.xml
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/pom.xml
@@ -15,6 +15,10 @@
basyx.aasdiscoveryservice-backend
+
+ org.eclipse.digitaltwin.basyx
+ basyx.backend
+
org.eclipse.digitaltwin.basyx
basyx.aasdiscoveryservice-core
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/SimpleAasDiscoveryFactory.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/SimpleAasDiscoveryFactory.java
index 6f552bbba..601c640d1 100644
--- a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/SimpleAasDiscoveryFactory.java
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/SimpleAasDiscoveryFactory.java
@@ -74,7 +74,7 @@ public SimpleAasDiscoveryFactory(AasDiscoveryBackendProvider aasBackendProvider,
@Override
public AasDiscoveryService create() {
- return new CrudAasDiscovery(aasBackendProvider, aasDiscoveryName);
+ return new ThreadSafeAasDiscovery(new CrudAasDiscovery(aasBackendProvider, aasDiscoveryName));
}
}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/ThreadSafeAasDiscovery.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/ThreadSafeAasDiscovery.java
new file mode 100644
index 000000000..bf118f0c9
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/backend/ThreadSafeAasDiscovery.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (C) 2024 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.aasdiscoveryservice.backend;
+
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.SpecificAssetId;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryService;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.model.AssetLink;
+import org.eclipse.digitaltwin.basyx.common.backend.InstanceScopedThreadSafeAccess;
+import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+
+/**
+ * A thread-safe wrapper for the {@link AasDiscoveryService}
+ *
+ * @author mateusmolina
+ */
+public class ThreadSafeAasDiscovery implements AasDiscoveryService {
+
+ private final AasDiscoveryService decoratedAasDiscovery;
+ private final InstanceScopedThreadSafeAccess access = new InstanceScopedThreadSafeAccess();
+
+ public ThreadSafeAasDiscovery(AasDiscoveryService decoratedAasDiscovery) {
+ this.decoratedAasDiscovery = decoratedAasDiscovery;
+ }
+
+ @Override
+ public CursorResult> getAllAssetAdministrationShellIdsByAssetLink(PaginationInfo pInfo, List assetIds) {
+ return decoratedAasDiscovery.getAllAssetAdministrationShellIdsByAssetLink(pInfo, assetIds);
+ }
+
+ @Override
+ public List getAllAssetLinksById(String shellIdentifier) {
+ return access.read(() -> decoratedAasDiscovery.getAllAssetLinksById(shellIdentifier), shellIdentifier);
+ }
+
+ @Override
+ public List createAllAssetLinksById(String shellIdentifier, List assetIds) {
+ return access.write(() -> decoratedAasDiscovery.createAllAssetLinksById(shellIdentifier, assetIds), shellIdentifier);
+ }
+
+ @Override
+ public void deleteAllAssetLinksById(String shellIdentifier) {
+ access.write(() -> decoratedAasDiscovery.deleteAllAssetLinksById(shellIdentifier), shellIdentifier);
+ }
+
+ @Override
+ public String getName() {
+ return decoratedAasDiscovery.getName();
+ }
+}
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..8ff6b6948
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (C) 2024 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(submodelId, getConnectedAasManager().getSubmodelService(submodelId).getSubmodel().getId());
+ assertTrue(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..001301975 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
@@ -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..d92b08272
--- /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) 2024 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.aasregistry/basyx.aasregistry-service-inmemory-storage/pom.xml b/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/pom.xml
index 3be02e79d..2ddafd618 100644
--- a/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/pom.xml
+++ b/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/pom.xml
@@ -17,6 +17,10 @@
jar
+
+ org.eclipse.digitaltwin.basyx
+ basyx.backend
+
org.eclipse.digitaltwin.basyx
basyx.aasregistry-service
diff --git a/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAasRegistryStorageDecorator.java b/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAasRegistryStorageDecorator.java
index 471588c6d..08a7041f2 100644
--- a/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAasRegistryStorageDecorator.java
+++ b/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAasRegistryStorageDecorator.java
@@ -38,6 +38,7 @@
import org.eclipse.digitaltwin.basyx.aasregistry.service.errors.SubmodelNotFoundException;
import org.eclipse.digitaltwin.basyx.aasregistry.service.storage.AasRegistryStorage;
import org.eclipse.digitaltwin.basyx.aasregistry.service.storage.DescriptorFilter;
+import org.eclipse.digitaltwin.basyx.common.backend.ThreadSafeAccess;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
@@ -50,37 +51,37 @@ public class ThreadSafeAasRegistryStorageDecorator implements AasRegistryStorage
@Override
public CursorResult> getAllAasDescriptors(@NonNull PaginationInfo pRequest, @NonNull DescriptorFilter filter) {
- return access.read(storage::getAllAasDescriptors, pRequest, filter);
+ return access.read(() -> storage.getAllAasDescriptors(pRequest, filter));
}
@Override
public void removeAasDescriptor(@NonNull String aasDescriptorId) {
- access.write(storage::removeAasDescriptor, aasDescriptorId);
+ access.write(() -> storage.removeAasDescriptor(aasDescriptorId));
}
@Override
public AssetAdministrationShellDescriptor getAasDescriptor(@NonNull String aasDescriptorId) throws AasDescriptorNotFoundException {
- return access.read(storage::getAasDescriptor, aasDescriptorId);
+ return access.read(() -> storage.getAasDescriptor(aasDescriptorId));
}
@Override
public CursorResult> getAllSubmodels(@NonNull String aasDescriptorId, @NonNull PaginationInfo pRequest) throws AasDescriptorNotFoundException {
- return access.read(storage::getAllSubmodels, aasDescriptorId, pRequest);
+ return access.read(() -> storage.getAllSubmodels(aasDescriptorId, pRequest));
}
@Override
public SubmodelDescriptor getSubmodel(@NonNull String aasDescriptorId, @NonNull String submodelId) {
- return access.read(storage::getSubmodel, aasDescriptorId, submodelId);
+ return access.read(() -> storage.getSubmodel(aasDescriptorId, submodelId));
}
@Override
public void insertSubmodel(@NonNull String aasDescriptorId, @NonNull SubmodelDescriptor submodel) {
- access.write(storage::insertSubmodel, aasDescriptorId, submodel);
+ access.write(() -> storage.insertSubmodel(aasDescriptorId, submodel));
}
@Override
public void removeSubmodel(@NonNull String aasDescrId, @NonNull String submodelId) {
- access.write(storage::removeSubmodel, aasDescrId, submodelId);
+ access.write(() -> storage.removeSubmodel(aasDescrId, submodelId));
}
@Override
@@ -90,21 +91,21 @@ public Set clear() {
@Override
public ShellDescriptorSearchResponse searchAasDescriptors(ShellDescriptorSearchRequest request) {
- return access.read(storage::searchAasDescriptors, request);
+ return access.read(() -> storage.searchAasDescriptors(request));
}
@Override
public void insertAasDescriptor(@Valid AssetAdministrationShellDescriptor descr) throws AasDescriptorAlreadyExistsException {
- access.write(storage::insertAasDescriptor, descr);
+ access.write(() -> storage.insertAasDescriptor(descr));
}
@Override
public void replaceAasDescriptor(@NonNull String aasDescritorId, @NonNull AssetAdministrationShellDescriptor descriptor) throws AasDescriptorNotFoundException {
- access.write(storage::replaceAasDescriptor, aasDescritorId, descriptor);
+ access.write(() -> storage.replaceAasDescriptor(aasDescritorId, descriptor));
}
@Override
public void replaceSubmodel(@NonNull String aasDescriptorId, @NonNull String submodelId, @NonNull SubmodelDescriptor submodel) throws AasDescriptorNotFoundException, SubmodelNotFoundException {
- access.write(storage::replaceSubmodel, aasDescriptorId, submodelId, submodel);
+ access.write(() -> storage.replaceSubmodel(aasDescriptorId, submodelId, submodel));
}
}
diff --git a/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAccess.java b/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAccess.java
deleted file mode 100644
index 3956c8977..000000000
--- a/basyx.aasregistry/basyx.aasregistry-service-inmemory-storage/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/memory/ThreadSafeAccess.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2023 DFKI GmbH (https://www.dfki.de/en/web)
- *
- * 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.aasregistry.service.storage.memory;
-
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-class ThreadSafeAccess {
-
- private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- private final ReadLock readLock = lock.readLock();
- private final WriteLock writeLock = lock.writeLock();
-
- public T write(Supplier supplier) {
- return runWithLock(supplier, writeLock);
- }
-
- public void write(Consumer consumer, A arg1) {
- runWithLock(consumer, arg1, readLock);
- }
-
- public void write(BiConsumer consumer, A arg1, B arg2) {
- runWithLock(consumer, arg1, arg2, writeLock);
- }
-
- public void write(TriConsumer consumer, A arg1, B arg2, C arg3) {
- runWithLock(consumer, arg1, arg2, arg3, writeLock);
- }
-
- public T read(Function func, A arg1) {
- return runWithLock(func, arg1, readLock);
- }
-
- public T read(BiFunction func, A arg1, B arg2) {
- return runWithLock(func, arg1, arg2, readLock);
- }
-
- private T runWithLock(Supplier supplier, Lock lock) {
- try {
- lock.lock();
- return supplier.get();
- } finally {
- lock.unlock();
- }
- }
-
- private void runWithLock(Consumer consumer, A arg1, Lock lock) {
- try {
- lock.lock();
- consumer.accept(arg1);
- } finally {
- lock.unlock();
- }
- }
-
- private T runWithLock(Function func, A arg1, Lock lock) {
- try {
- lock.lock();
- return func.apply(arg1);
- } finally {
- lock.unlock();
- }
- }
-
- private T runWithLock(BiFunction func, A arg1, B arg2, Lock lock) {
- try {
- lock.lock();
- return func.apply(arg1, arg2);
- } finally {
- lock.unlock();
- }
- }
-
- private void runWithLock(BiConsumer consumer, A arg1, B arg2, Lock lock) {
- try {
- lock.lock();
- consumer.accept(arg1, arg2);
- } finally {
- lock.unlock();
- }
- }
-
- private void runWithLock(TriConsumer consumer, A arg1, B arg2, C arg3, Lock lock) {
- try {
- lock.lock();
- consumer.accept(arg1, arg2, arg3);
- } finally {
- lock.unlock();
- }
- }
-
- @FunctionalInterface
- public static interface TriConsumer {
-
- void accept(S s, T t, U u);
-
- }
-}
\ No newline at end of file
diff --git a/basyx.aasrepository/basyx.aasrepository-backend/pom.xml b/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
index 5936dd14a..2c6e699ce 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
+++ b/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
@@ -15,6 +15,10 @@
basyx.aasrepository-backend
+
+ org.eclipse.digitaltwin.basyx
+ basyx.backend
+
org.eclipse.digitaltwin.basyx
basyx.aasrepository-core
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/SimpleAasRepositoryFactory.java
index c9bbdb3c3..a23059ca2 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/SimpleAasRepositoryFactory.java
@@ -64,7 +64,7 @@ public SimpleAasRepositoryFactory(AasBackendProvider aasBackendProvider, AasServ
@Override
public AasRepository create() {
- return new CrudAasRepository(aasBackendProvider, aasServiceFactory, aasRepositoryName);
+ return new ThreadSafeAasRepository(new CrudAasRepository(aasBackendProvider, aasServiceFactory, aasRepositoryName));
}
}
diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/ThreadSafeAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/ThreadSafeAasRepository.java
new file mode 100644
index 000000000..842176907
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/ThreadSafeAasRepository.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (C) 2024 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 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.aasrepository.AasRepository;
+import org.eclipse.digitaltwin.basyx.common.backend.InstanceScopedThreadSafeAccess;
+import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException;
+import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+
+/**
+ * A thread-safe wrapper for the {@link AasRepository}
+ *
+ * @author mateusmolina
+ */
+public class ThreadSafeAasRepository implements AasRepository {
+
+ private final AasRepository decoratedAasRepository;
+ private final InstanceScopedThreadSafeAccess access = new InstanceScopedThreadSafeAccess();
+
+ public ThreadSafeAasRepository(AasRepository decoratedRepository) {
+ this.decoratedAasRepository = decoratedRepository;
+ }
+
+ @Override
+ public CursorResult> getAllAas(PaginationInfo pInfo) {
+ return decoratedAasRepository.getAllAas(pInfo);
+ }
+
+ @Override
+ public AssetAdministrationShell getAas(String aasId) throws ElementDoesNotExistException {
+ return access.read(() -> decoratedAasRepository.getAas(aasId), aasId);
+ }
+
+ @Override
+ public void createAas(AssetAdministrationShell aas) throws CollidingIdentifierException, MissingIdentifierException {
+ decoratedAasRepository.createAas(aas);
+ }
+
+ @Override
+ public void deleteAas(String aasId) {
+ access.write(() -> decoratedAasRepository.deleteAas(aasId), aasId);
+ }
+
+ @Override
+ public void updateAas(String aasId, AssetAdministrationShell aas) {
+ access.write(() -> decoratedAasRepository.updateAas(aasId, aas), aasId);
+ }
+
+ @Override
+ public CursorResult> getSubmodelReferences(String aasId, PaginationInfo pInfo) {
+ return access.read(() -> decoratedAasRepository.getSubmodelReferences(aasId, pInfo), aasId);
+ }
+
+ @Override
+ public void addSubmodelReference(String aasId, Reference submodelReference) {
+ access.write(() -> decoratedAasRepository.addSubmodelReference(aasId, submodelReference), aasId);
+ }
+
+ @Override
+ public void removeSubmodelReference(String aasId, String submodelId) {
+ access.write(() -> decoratedAasRepository.removeSubmodelReference(aasId, submodelId), aasId);
+ }
+
+ @Override
+ public void setAssetInformation(String aasId, AssetInformation aasInfo) throws ElementDoesNotExistException {
+ access.write(() -> decoratedAasRepository.setAssetInformation(aasId, aasInfo), aasId);
+ }
+
+ @Override
+ public AssetInformation getAssetInformation(String aasId) throws ElementDoesNotExistException {
+ return access.read(() -> decoratedAasRepository.getAssetInformation(aasId), aasId);
+ }
+
+ @Override
+ public File getThumbnail(String aasId) {
+ return access.read(() -> decoratedAasRepository.getThumbnail(aasId), aasId);
+ }
+
+ @Override
+ public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) {
+ access.write(() -> decoratedAasRepository.setThumbnail(aasId, fileName, contentType, inputStream), aasId);
+ }
+
+ @Override
+ public void deleteThumbnail(String aasId) {
+ access.write(() -> decoratedAasRepository.deleteThumbnail(aasId), aasId);
+ }
+
+ @Override
+ public String getName() {
+ return decoratedAasRepository.getName();
+ }
+
+}
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..1421ed428 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
@@ -90,31 +90,27 @@ public CursorResult> getSubmodelReferences(PaginationInfo pInfo)
return paginatedSubmodelReference;
}
-
@Override
public void addSubmodelReference(Reference submodelReference) {
+ throwExceptionIfReferenceIsAlreadyPresent(submodelReference);
+
List submodelsRefs = aas.getSubmodels();
- synchronized (submodelsRefs) {
- throwExceptionIfReferenceIsAlreadyPresent(submodelReference);
- submodelsRefs.add(submodelReference);
- }
+ submodelsRefs.add(submodelReference);
}
@Override
public void removeSubmodelReference(String submodelId) {
List submodelsRefs = aas.getSubmodels();
- synchronized (submodelsRefs) {
- submodelsRefs.remove(getSubmodelReferenceById(submodelId));
- }
+ submodelsRefs.remove(getSubmodelReferenceById(submodelId));
}
@Override
public void setAssetInformation(AssetInformation aasInfo) {
- aas.setAssetInformation(aasInfo);
+ aas.setAssetInformation(aasInfo);
}
-
+
@Override
- public AssetInformation getAssetInformation() {
+ public AssetInformation getAssetInformation() {
return aas.getAssetInformation();
}
@@ -130,10 +126,8 @@ private Reference getSubmodelReferenceById(String submodelId) {
return specificSubmodelReference;
}
- private TreeMap convertToTreeMap(List submodelReferences,
- Function idResolver) {
- return submodelReferences.stream().collect(Collectors
- .toMap(reference -> idResolver.apply(reference), ref -> ref, (ref1, ref2) -> ref1, TreeMap::new));
+ private TreeMap convertToTreeMap(List submodelReferences, Function idResolver) {
+ return submodelReferences.stream().collect(Collectors.toMap(reference -> idResolver.apply(reference), ref -> ref, (ref1, ref2) -> ref1, TreeMap::new));
}
private Function extractSubmodelID() {
@@ -165,9 +159,9 @@ public File getThumbnail() {
public void setThumbnail(String fileName, String contentType, InputStream inputStream) {
FileMetadata thumbnailMetadata = new FileMetadata(fileName, contentType, inputStream);
- if(fileRepository.exists(thumbnailMetadata.getFileName()))
+ if (fileRepository.exists(thumbnailMetadata.getFileName()))
fileRepository.delete(thumbnailMetadata.getFileName());
-
+
String filePath = fileRepository.save(thumbnailMetadata);
setAssetInformation(configureAssetInformationThumbnail(getAssetInformation(), contentType, filePath));
@@ -218,7 +212,7 @@ private File getResourceContent(Resource resource) throws IOException {
private void throwExceptionIfReferenceIsAlreadyPresent(Reference submodelReference) {
Optional submodelIdKey = getSubmodelTypeKey(submodelReference);
- if(submodelIdKey.isEmpty())
+ if (submodelIdKey.isEmpty())
return;
String submodelId = submodelIdKey.get().getValue();
if (isSubmodelIdAlreadyReferenced(submodelId)) {
@@ -233,7 +227,7 @@ private boolean isSubmodelIdAlreadyReferenced(String submodelId) {
private static Optional getSubmodelTypeKey(Reference submodelReference) {
Optional submodelIdKey = submodelReference.getKeys().stream().filter(key -> {
KeyTypes type = key.getType();
- if(type == null)
+ if (type == null)
throw new MissingKeyTypeException();
return type.equals(KeyTypes.SUBMODEL);
}).findFirst();
diff --git a/basyx.aasxfileserver/basyx.aasxfileserver-backend/pom.xml b/basyx.aasxfileserver/basyx.aasxfileserver-backend/pom.xml
index f142caf38..9b9d6fa9e 100644
--- a/basyx.aasxfileserver/basyx.aasxfileserver-backend/pom.xml
+++ b/basyx.aasxfileserver/basyx.aasxfileserver-backend/pom.xml
@@ -9,6 +9,10 @@
BaSyx AASX File Server Backend
+
+ org.eclipse.digitaltwin.basyx
+ basyx.backend
+
org.eclipse.digitaltwin.basyx
basyx.aasxfileserver-core
diff --git a/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/SimpleAASXFileServerFactory.java b/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/SimpleAASXFileServerFactory.java
index 58d4dd093..6131aed5f 100644
--- a/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/SimpleAASXFileServerFactory.java
+++ b/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/SimpleAASXFileServerFactory.java
@@ -74,7 +74,7 @@ public SimpleAASXFileServerFactory(AASXFileServerBackendProvider aasxFileServerB
@Override
public AASXFileServer create() {
- return new CrudAASXFileServer(aasxFileServerBackendProvider, aasxFileServerName);
+ return new ThreadSafeAASXFileServer(new CrudAASXFileServer(aasxFileServerBackendProvider, aasxFileServerName));
}
}
diff --git a/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/ThreadSafeAASXFileServer.java b/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/ThreadSafeAASXFileServer.java
new file mode 100644
index 000000000..79051c929
--- /dev/null
+++ b/basyx.aasxfileserver/basyx.aasxfileserver-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/backend/ThreadSafeAASXFileServer.java
@@ -0,0 +1,83 @@
+
+/*******************************************************************************
+ * Copyright (C) 2024 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.aasxfileserver.backend;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.PackageDescription;
+import org.eclipse.digitaltwin.basyx.aasxfileserver.AASXFileServer;
+import org.eclipse.digitaltwin.basyx.common.backend.InstanceScopedThreadSafeAccess;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+
+/**
+ * A thread-safe wrapper for the {@link AASXFileServer}
+ *
+ * @author mateusmolina
+ */
+public class ThreadSafeAASXFileServer implements AASXFileServer {
+
+ private final AASXFileServer decoratedAasxFileServer;
+ private final InstanceScopedThreadSafeAccess access = new InstanceScopedThreadSafeAccess();
+
+ public ThreadSafeAASXFileServer(AASXFileServer aasxFileServer) {
+ this.decoratedAasxFileServer = aasxFileServer;
+ }
+
+ @Override
+ public CursorResult> getAllAASXPackageIds(String shellId, PaginationInfo pInfo) {
+ return decoratedAasxFileServer.getAllAASXPackageIds(shellId, pInfo);
+ }
+
+ @Override
+ public InputStream getAASXByPackageId(String packageId) throws ElementDoesNotExistException {
+ return access.read(() -> decoratedAasxFileServer.getAASXByPackageId(packageId), packageId);
+ }
+
+ @Override
+ public void updateAASXByPackageId(String packageId, List shellIds, InputStream file, String filename) throws ElementDoesNotExistException {
+ access.write(() -> decoratedAasxFileServer.updateAASXByPackageId(packageId, shellIds, file, filename), packageId);
+ }
+
+ @Override
+ public PackageDescription createAASXPackage(List shellIds, InputStream file, String filename) {
+ return decoratedAasxFileServer.createAASXPackage(shellIds, file, filename);
+ }
+
+ @Override
+ public void deleteAASXByPackageId(String packageId) throws ElementDoesNotExistException {
+ access.write(() -> decoratedAasxFileServer.deleteAASXByPackageId(packageId), packageId);
+ }
+
+ @Override
+ public String getName() {
+ return decoratedAasxFileServer.getName();
+ }
+
+}
diff --git a/basyx.common/basyx.backend/pom.xml b/basyx.common/basyx.backend/pom.xml
new file mode 100644
index 000000000..59a5f257b
--- /dev/null
+++ b/basyx.common/basyx.backend/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.common
+ ${revision}
+
+ basyx.backend
+ BaSyx Backend Common
+ BaSyx Backend Common
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.core
+
+
+
\ No newline at end of file
diff --git a/basyx.common/basyx.backend/src/main/java/org/eclipse/digitaltwin/basyx/common/backend/InstanceScopedThreadSafeAccess.java b/basyx.common/basyx.backend/src/main/java/org/eclipse/digitaltwin/basyx/common/backend/InstanceScopedThreadSafeAccess.java
new file mode 100644
index 000000000..40e0a5601
--- /dev/null
+++ b/basyx.common/basyx.backend/src/main/java/org/eclipse/digitaltwin/basyx/common/backend/InstanceScopedThreadSafeAccess.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (C) 2024 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.common.backend;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Utility class for thread-safe access at the instance level
+ *
+ * @author mateusmolina
+ */
+public class InstanceScopedThreadSafeAccess {
+ private final ConcurrentHashMap