Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Backend Architecture #570

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<String> createdSubmodelIds = new ConcurrentLinkedDeque<>();

List<Future<Boolean>> 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);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<String> createdSubmodelIds = new ConcurrentLinkedDeque<>();

List<Future<Boolean>> 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();
Expand All @@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +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.aasrepository.backend.inmemory.AasInMemoryBackendConfiguration;
import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository;
Expand Down Expand Up @@ -77,7 +77,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(new AasInMemoryBackendConfiguration(), new InMemoryAasServiceFactory(new InMemoryFileRepository())));
conceptDescriptionRepository = Mockito.spy(new CrudConceptDescriptionRepository(new ConceptDescriptionInMemoryBackendProvider()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory;
import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleConceptDescriptionRepositoryFactory;
import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendConfiguration;
import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository;
Expand Down Expand Up @@ -88,7 +88,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 SimpleAasRepositoryFactory(new AasInMemoryBackendConfiguration(), new InMemoryAasServiceFactory(new InMemoryFileRepository())).create();
conceptDescriptionRepository = new SimpleConceptDescriptionRepositoryFactory(new ConceptDescriptionInMemoryBackendProvider(), createDummyConceptDescriptions(), "cdRepo").create();

for (Submodel submodel : createDummySubmodels()) {
Expand Down
Loading
Loading