From 732be46a71b5bc031fcba41c9fabb0c4e935dfc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:14:18 +0100 Subject: [PATCH 01/17] Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.1 to 3.5.2 (#526) Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.5.1 to 3.5.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.1...surefire-3.5.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bc486d770..eda86ca46 100644 --- a/pom.xml +++ b/pom.xml @@ -170,7 +170,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.1 + 3.5.2 integration-test From 8f4eafa0fd6fa2cf8b410716a2b22594820a0abd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:15:24 +0100 Subject: [PATCH 02/17] Bump org.apache.tika:tika-core from 2.9.2 to 3.0.0 (#527) Bumps [org.apache.tika:tika-core](https://github.com/apache/tika) from 2.9.2 to 3.0.0. - [Changelog](https://github.com/apache/tika/blob/main/CHANGES.txt) - [Commits](https://github.com/apache/tika/compare/2.9.2...3.0.0) --- updated-dependencies: - dependency-name: org.apache.tika:tika-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eda86ca46..36dd3ae3b 100644 --- a/pom.xml +++ b/pom.xml @@ -377,7 +377,7 @@ org.apache.tika tika-core - 2.9.2 + 3.0.0 org.reflections From faefac4a005d2f4cad32504521cdf8aaabc2a505 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:16:02 +0100 Subject: [PATCH 03/17] Bump com.google.guava:guava from 33.2.1-jre to 33.3.1-jre (#539) Bumps [com.google.guava:guava](https://github.com/google/guava) from 33.2.1-jre to 33.3.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- basyx.aasenvironment/basyx.aasenvironment-core/pom.xml | 2 +- basyx.aasregistry/pom.xml | 2 +- basyx.submodelregistry/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/pom.xml b/basyx.aasenvironment/basyx.aasenvironment-core/pom.xml index 3d8a5b48b..75b8d57fa 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/pom.xml +++ b/basyx.aasenvironment/basyx.aasenvironment-core/pom.xml @@ -102,7 +102,7 @@ com.google.guava guava - 33.2.1-jre + 33.3.1-jre diff --git a/basyx.aasregistry/pom.xml b/basyx.aasregistry/pom.xml index 5f037ad07..6cf1072d5 100644 --- a/basyx.aasregistry/pom.xml +++ b/basyx.aasregistry/pom.xml @@ -27,7 +27,7 @@ Plattform_i40-AssetAdministrationShellRegistryServiceSpecification-V3.0.1_SSP-001-resolved.yaml Plattform_i40-Registry-and-Discovery.yaml - 33.2.1-jre + 33.3.1-jre 3.9.0 3.0-alpha-2 0.9.14 diff --git a/basyx.submodelregistry/pom.xml b/basyx.submodelregistry/pom.xml index 197bd6dd6..8cb518ecb 100644 --- a/basyx.submodelregistry/pom.xml +++ b/basyx.submodelregistry/pom.xml @@ -25,7 +25,7 @@ Plattform_i40-SubmodelRegistryServiceSpecification-V3.0.1_SSP-001-resolved.yaml Plattform_i40-SubmodelRegistry-and-Discovery.yaml - 33.2.1-jre + 33.3.1-jre 3.6.0 3.0-alpha-2 0.9.14 From 23bae6f06130cff3ece55526762b2030c01588a0 Mon Sep 17 00:00:00 2001 From: Mateus Molina Date: Mon, 25 Nov 2024 13:20:52 +0100 Subject: [PATCH 04/17] Fix OperationDelegation example not working (424 error) (#536) * fix: add csrf whitelist for operation-invocation endpoint * chore: bump comp versions --- .../BaSyxOperationDelegation/docker-compose.yml | 10 +++++----- .../exampleOperationService/pom.xml | 4 ++-- .../configuration/SecurityConfiguration.java | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 examples/BaSyxOperationDelegation/exampleOperationService/src/main/java/org/eclipse/digitaltwin/basyx/examples/operationdelegation/configuration/SecurityConfiguration.java diff --git a/examples/BaSyxOperationDelegation/docker-compose.yml b/examples/BaSyxOperationDelegation/docker-compose.yml index 388c88f33..146ec035c 100644 --- a/examples/BaSyxOperationDelegation/docker-compose.yml +++ b/examples/BaSyxOperationDelegation/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: aas-env: - image: eclipsebasyx/aas-environment:2.0.0-milestone-03 + image: eclipsebasyx/aas-environment:2.0.0-milestone-04 container_name: aas-env volumes: - ./aas:/application/aas @@ -15,7 +15,7 @@ services: sm-registry: condition: service_healthy aas-registry: - image: eclipsebasyx/aas-registry-log-mem:2.0.0-milestone-03 + image: eclipsebasyx/aas-registry-log-mem:2.0.0-milestone-04 container_name: aas-registry ports: - '8082:8080' @@ -23,7 +23,7 @@ services: - ./basyx/aas-registry.yml:/workspace/config/application.yml restart: always sm-registry: - image: eclipsebasyx/submodel-registry-log-mem:2.0.0-milestone-03 + image: eclipsebasyx/submodel-registry-log-mem:2.0.0-milestone-04 container_name: sm-registry ports: - '8083:8080' @@ -31,7 +31,7 @@ services: - ./basyx/sm-registry.yml:/workspace/config/application.yml restart: always aas-web-ui: - image: eclipsebasyx/aas-gui:v2-240801 + image: eclipsebasyx/aas-gui:v2-241114 container_name: aas-ui ports: - '3000:3000' @@ -49,7 +49,7 @@ services: build: context: ./exampleOperationService dockerfile: Dockerfile - container_name: example-operation + container_name: example-operation-service ports: - '8087:8080' restart: always diff --git a/examples/BaSyxOperationDelegation/exampleOperationService/pom.xml b/examples/BaSyxOperationDelegation/exampleOperationService/pom.xml index 98ee4b0a1..bb35bc61e 100644 --- a/examples/BaSyxOperationDelegation/exampleOperationService/pom.xml +++ b/examples/BaSyxOperationDelegation/exampleOperationService/pom.xml @@ -16,7 +16,7 @@ BaSyx Operation Delegation Example 17 - 1.0.1 + 1.0.2 @@ -49,7 +49,7 @@ org.eclipse.digitaltwin.basyx basyx.submodelrepository-client - 2.0.0-milestone-03 + 2.0.0-milestone-04 diff --git a/examples/BaSyxOperationDelegation/exampleOperationService/src/main/java/org/eclipse/digitaltwin/basyx/examples/operationdelegation/configuration/SecurityConfiguration.java b/examples/BaSyxOperationDelegation/exampleOperationService/src/main/java/org/eclipse/digitaltwin/basyx/examples/operationdelegation/configuration/SecurityConfiguration.java new file mode 100644 index 000000000..85d109853 --- /dev/null +++ b/examples/BaSyxOperationDelegation/exampleOperationService/src/main/java/org/eclipse/digitaltwin/basyx/examples/operationdelegation/configuration/SecurityConfiguration.java @@ -0,0 +1,17 @@ +package org.eclipse.digitaltwin.basyx.examples.operationdelegation.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfiguration { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + httpSecurity.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()).httpBasic(Customizer.withDefaults()).csrf(c -> c.ignoringRequestMatchers("/operation-invocation")); + + return httpSecurity.build(); + } +} From 912dc79b43d1c86a3931e3e3dc85caeb930245d6 Mon Sep 17 00:00:00 2001 From: Sebastian Eicke Date: Mon, 25 Nov 2024 13:55:22 +0100 Subject: [PATCH 05/17] Add Profile description for AAS Discovery Service (#534) --- ...iscoveryServiceDescriptionConfiguration.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-http/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/http/AasDiscoveryServiceDescriptionConfiguration.java diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-http/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/http/AasDiscoveryServiceDescriptionConfiguration.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-http/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/http/AasDiscoveryServiceDescriptionConfiguration.java new file mode 100644 index 000000000..6a33d8b2d --- /dev/null +++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-http/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/http/AasDiscoveryServiceDescriptionConfiguration.java @@ -0,0 +1,17 @@ +package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.http; + +import java.util.List; +import java.util.TreeSet; + +import org.eclipse.digitaltwin.basyx.http.description.Profile; +import org.eclipse.digitaltwin.basyx.http.description.ProfileDeclaration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AasDiscoveryServiceDescriptionConfiguration { + @Bean + public ProfileDeclaration aasDiscoveryProfiles() { + return () -> new TreeSet<>(List.of(Profile.DISCOVERYSERVICESPECIFICATION_SSP_001)); + } +} \ No newline at end of file From 4d1a6438336252e745c316e2751bc8815762ddb0 Mon Sep 17 00:00:00 2001 From: Sebastian Eicke Date: Mon, 25 Nov 2024 15:15:53 +0100 Subject: [PATCH 06/17] Add Profile description for AASX File Server (#543) --- .../AASXFileServerDescriptionConfiguration.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 basyx.aasxfileserver/basyx.aasxfileserver-http/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/http/AASXFileServerDescriptionConfiguration.java diff --git a/basyx.aasxfileserver/basyx.aasxfileserver-http/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/http/AASXFileServerDescriptionConfiguration.java b/basyx.aasxfileserver/basyx.aasxfileserver-http/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/http/AASXFileServerDescriptionConfiguration.java new file mode 100644 index 000000000..1d4c88c95 --- /dev/null +++ b/basyx.aasxfileserver/basyx.aasxfileserver-http/src/main/java/org/eclipse/digitaltwin/basyx/aasxfileserver/http/AASXFileServerDescriptionConfiguration.java @@ -0,0 +1,17 @@ +package org.eclipse.digitaltwin.basyx.aasxfileserver.http; + +import java.util.List; +import java.util.TreeSet; + +import org.eclipse.digitaltwin.basyx.http.description.Profile; +import org.eclipse.digitaltwin.basyx.http.description.ProfileDeclaration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AASXFileServerDescriptionConfiguration { + @Bean + public ProfileDeclaration aasxFileServerProfiles() { + return () -> new TreeSet<>(List.of(Profile.AASXFILESERVERSERVICESPECIFICATION_SSP_001)); + } +} \ No newline at end of file From fa33413e5dd7e2624ea1753f39eccfea70fc59d8 Mon Sep 17 00:00:00 2001 From: Mateus Molina Date: Thu, 28 Nov 2024 16:03:47 +0100 Subject: [PATCH 07/17] Fix issue with the parallel execution of the `ConnectedAasManager::createSubmodelInAas` method. (#547) * test: failing test for ConnectedAasManager parallel exec * refactor: desc factory classes * test: add aas refId assertion * fix: instances of leg. usage of *DescFactory Api * fix: conflicts while adding smRefs to shells in parallel * fix: other sync issues in InMemorySubmodelService * fix: add Sync Lock to InMemorySubmodelService * chore: add missing license header --- .../client/ConnectedAasManagerHelper.java | 4 +- ...TestConnectedAasManagerMultithreading.java | 158 ++++++++++++++++++ .../client/factory/AasDescriptorFactory.java | 46 +++-- .../RegistryIntegrationAasRepository.java | 2 +- ...yRegistryLinkDescriptorGenerationTest.java | 4 +- .../backend/InMemoryAasService.java | 15 +- .../factory/SubmodelDescriptorFactory.java | 44 ++--- ...RegistryIntegrationSubmodelRepository.java | 2 +- ...yRegistryLinkDescriptorGenerationTest.java | 4 +- .../InMemorySubmodelService.java | 123 ++++++++------ 10 files changed, 280 insertions(+), 122 deletions(-) create mode 100644 basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreading.java diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java index d6e0ab8c6..3d8a90ec7 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java @@ -60,13 +60,13 @@ static ObjectMapper buildObjectMapper() { static AasDescriptorFactory buildAasDescriptorFactory(String... aasRepositoryBaseUrls) { AttributeMapper attributeMapper = new AttributeMapper(objectMapper); - return new AasDescriptorFactory(null, List.of(aasRepositoryBaseUrls), attributeMapper); + return new AasDescriptorFactory(List.of(aasRepositoryBaseUrls), attributeMapper); } static SubmodelDescriptorFactory buildSmDescriptorFactory(String... aasRepositoryBaseUrls) { org.eclipse.digitaltwin.basyx.submodelregistry.client.mapper.AttributeMapper attributeMapperSm = new org.eclipse.digitaltwin.basyx.submodelregistry.client.mapper.AttributeMapper( objectMapper); - return new SubmodelDescriptorFactory(null, List.of(aasRepositoryBaseUrls), attributeMapperSm); + return new SubmodelDescriptorFactory(List.of(aasRepositoryBaseUrls), attributeMapperSm); } } 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/TestConnectedAasManagerMultithreading.java new file mode 100644 index 000000000..8ff0ea860 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManagerMultithreading.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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.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 { + 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 ConnectedAasRepository connectedAasRepository; + static ConnectedSubmodelRepository connectedSmRepository; + static RegistryAndDiscoveryInterfaceApi aasRegistryApi; + static SubmodelRegistryApi smRegistryApi; + + static ConnectedAasManager aasManager; + + @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); + 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(); + } + + @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(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + try { + smRegistryApi.deleteAllSubmodelDescriptors(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + 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(); + } + + 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); + } + } + +} diff --git a/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java index 723b6fbea..1fe0709bb 100644 --- a/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java +++ b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java @@ -53,15 +53,12 @@ public class AasDescriptorFactory { private static final String AAS_INTERFACE = "AAS-3.0"; private static final String AAS_REPOSITORY_PATH = "shells"; - private AssetAdministrationShell shell; - private List aasRepositoryURLs; + private final List aasRepositoryURLs; + private static AttributeMapper attributeMapper; - private AttributeMapper attributeMapper; - - public AasDescriptorFactory(AssetAdministrationShell shell, List aasRepositoryBaseURLs, AttributeMapper attributeMapper) { - this.shell = shell; + public AasDescriptorFactory(List aasRepositoryBaseURLs, AttributeMapper attributeMapper) { this.aasRepositoryURLs = createAasRepositoryUrls(aasRepositoryBaseURLs); - this.attributeMapper = attributeMapper; + AasDescriptorFactory.attributeMapper = attributeMapper; } /** @@ -69,7 +66,7 @@ public AasDescriptorFactory(AssetAdministrationShell shell, List aasRepo * * @return the created AssetAdministrationShellDescriptor */ - public AssetAdministrationShellDescriptor create() { + public AssetAdministrationShellDescriptor create(AssetAdministrationShell shell) { AssetAdministrationShellDescriptor descriptor = new AssetAdministrationShellDescriptor(); @@ -77,7 +74,7 @@ public AssetAdministrationShellDescriptor create() { setIdShort(shell.getIdShort(), descriptor); - setEndpointItem(shell.getId(), descriptor); + setEndpointItem(shell.getId(), descriptor, aasRepositoryURLs); setDescription(shell.getDescription(), descriptor); @@ -96,12 +93,7 @@ public AssetAdministrationShellDescriptor create() { return descriptor; } - public AssetAdministrationShellDescriptor create(AssetAdministrationShell shell) { - this.shell = shell; - return create(); - } - - private void setDescription(List descriptions, AssetAdministrationShellDescriptor descriptor) { + private static void setDescription(List descriptions, AssetAdministrationShellDescriptor descriptor) { if (descriptions == null || descriptions.isEmpty()) return; @@ -109,7 +101,7 @@ private void setDescription(List descriptions, AssetAdminist descriptor.setDescription(attributeMapper.mapDescription(descriptions)); } - private void setDisplayName(List displayNames, AssetAdministrationShellDescriptor descriptor) { + private static void setDisplayName(List displayNames, AssetAdministrationShellDescriptor descriptor) { if (displayNames == null || displayNames.isEmpty()) return; @@ -117,7 +109,7 @@ private void setDisplayName(List displayNames, AssetAdminist descriptor.setDisplayName(attributeMapper.mapDisplayName(displayNames)); } - private void setExtensions(List extensions, AssetAdministrationShellDescriptor descriptor) { + private static void setExtensions(List extensions, AssetAdministrationShellDescriptor descriptor) { if (extensions == null || extensions.isEmpty()) return; @@ -125,7 +117,7 @@ private void setExtensions(List extensions, AssetAdministrationShellD descriptor.setExtensions(attributeMapper.mapExtensions(extensions)); } - private void setAdministration(AdministrativeInformation administration, AssetAdministrationShellDescriptor descriptor) { + private static void setAdministration(AdministrativeInformation administration, AssetAdministrationShellDescriptor descriptor) { if (administration == null) return; @@ -133,7 +125,7 @@ private void setAdministration(AdministrativeInformation administration, AssetAd descriptor.setAdministration(attributeMapper.mapAdministration(administration)); } - private void setAssetKind(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + private static void setAssetKind(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { if (assetInformation == null || assetInformation.getAssetKind() == null) return; @@ -141,7 +133,7 @@ private void setAssetKind(AssetInformation assetInformation, AssetAdministration descriptor.setAssetKind(attributeMapper.mapAssetKind(assetInformation.getAssetKind())); } - private void setAssetType(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + private static void setAssetType(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { if (assetInformation == null || assetInformation.getAssetType() == null) return; @@ -149,7 +141,7 @@ private void setAssetType(AssetInformation assetInformation, AssetAdministration descriptor.setAssetType(assetInformation.getAssetType()); } - private void setGlobalAssetId(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + private static void setGlobalAssetId(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { if (assetInformation == null || assetInformation.getGlobalAssetId() == null) return; @@ -157,7 +149,7 @@ private void setGlobalAssetId(AssetInformation assetInformation, AssetAdministra descriptor.setGlobalAssetId(assetInformation.getGlobalAssetId()); } - private void setEndpointItem(String shellId, AssetAdministrationShellDescriptor descriptor) { + private static void setEndpointItem(String shellId, AssetAdministrationShellDescriptor descriptor, List aasRepositoryURLs) { for (String eachUrl : aasRepositoryURLs) { Endpoint endpoint = new Endpoint(); endpoint.setInterface(AAS_INTERFACE); @@ -168,7 +160,7 @@ private void setEndpointItem(String shellId, AssetAdministrationShellDescriptor } } - private ProtocolInformation createProtocolInformation(String shellId, String url) { + private static ProtocolInformation createProtocolInformation(String shellId, String url) { String href = String.format("%s/%s", url, Base64UrlEncodedIdentifier.encodeIdentifier(shellId)); ProtocolInformation protocolInformation = new ProtocolInformation(); @@ -178,15 +170,15 @@ private ProtocolInformation createProtocolInformation(String shellId, String url return protocolInformation; } - private void setIdShort(String idShort, AssetAdministrationShellDescriptor descriptor) { + private static void setIdShort(String idShort, AssetAdministrationShellDescriptor descriptor) { descriptor.setIdShort(idShort); } - private void setId(String shellId, AssetAdministrationShellDescriptor descriptor) { + private static void setId(String shellId, AssetAdministrationShellDescriptor descriptor) { descriptor.setId(shellId); } - private String getProtocol(String endpoint) { + private static String getProtocol(String endpoint) { try { return new URL(endpoint).getProtocol(); } catch (MalformedURLException e) { @@ -194,7 +186,7 @@ private String getProtocol(String endpoint) { } } - private List createAasRepositoryUrls(List aasRepositoryBaseURLs) { + private static List createAasRepositoryUrls(List aasRepositoryBaseURLs) { List toReturn = new ArrayList<>(aasRepositoryBaseURLs.size()); for (String eachUrl : aasRepositoryBaseURLs) { toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, AAS_REPOSITORY_PATH)); diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java index a938e76d4..f5a8a238c 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java @@ -78,7 +78,7 @@ public AssetAdministrationShell getAas(String shellId) throws ElementDoesNotExis @Override public void createAas(AssetAdministrationShell shell) throws CollidingIdentifierException { - AssetAdministrationShellDescriptor descriptor = new AasDescriptorFactory(shell, aasRepositoryRegistryLink.getAasRepositoryBaseURLs(), attributeMapper).create(); + AssetAdministrationShellDescriptor descriptor = new AasDescriptorFactory(aasRepositoryRegistryLink.getAasRepositoryBaseURLs(), attributeMapper).create(shell); decorated.createAas(shell); 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 7c751945b..8b3739033 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 @@ -103,8 +103,8 @@ public void testExternalUrl() { private AssetAdministrationShellDescriptor createAndRetrieveDescriptor(AssetAdministrationShell shell) { registryIntegrationAasRepository.createAas(shell); - AasDescriptorFactory descriptorFactory = new AasDescriptorFactory(shell, mockedRegistryLink.getAasRepositoryBaseURLs(), mockedAttributeMapper); - return descriptorFactory.create(); + AasDescriptorFactory descriptorFactory = new AasDescriptorFactory(mockedRegistryLink.getAasRepositoryBaseURLs(), mockedAttributeMapper); + return descriptorFactory.create(shell); } private AssetAdministrationShell createDummyAas() { 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 563dfc8f4..0fc9b9516 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 @@ -93,16 +93,19 @@ public CursorResult> getSubmodelReferences(PaginationInfo pInfo) @Override public void addSubmodelReference(Reference submodelReference) { - throwExceptionIfReferenceIsAlreadyPresent(submodelReference); - - aas.getSubmodels().add(submodelReference); + List submodelsRefs = aas.getSubmodels(); + synchronized (submodelsRefs) { + throwExceptionIfReferenceIsAlreadyPresent(submodelReference); + submodelsRefs.add(submodelReference); + } } @Override public void removeSubmodelReference(String submodelId) { - Reference specificSubmodelReference = getSubmodelReferenceById(submodelId); - - aas.getSubmodels().remove(specificSubmodelReference); + List submodelsRefs = aas.getSubmodels(); + synchronized (submodelsRefs) { + submodelsRefs.remove(getSubmodelReferenceById(submodelId)); + } } @Override diff --git a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java index e5487241c..a7376be7c 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java +++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java @@ -53,15 +53,12 @@ public class SubmodelDescriptorFactory { private static final String SUBMODEL_INTERFACE = "SUBMODEL-3.0"; private static final String SUBMODEL_REPOSITORY_PATH = "submodels"; - private Submodel submodel; - private List submodelRepositoryURLs; + private final List submodelRepositoryURLs; + private static AttributeMapper attributeMapper; - private AttributeMapper attributeMapper; - - public SubmodelDescriptorFactory(Submodel submodel, List submodelRepositoryBaseURLs, AttributeMapper attributeMapper) { - this.submodel = submodel; + public SubmodelDescriptorFactory(List submodelRepositoryBaseURLs, AttributeMapper attributeMapper) { this.submodelRepositoryURLs = createSubmodelRepositoryUrls(submodelRepositoryBaseURLs); - this.attributeMapper = attributeMapper; + SubmodelDescriptorFactory.attributeMapper = attributeMapper; } /** @@ -69,7 +66,7 @@ public SubmodelDescriptorFactory(Submodel submodel, List submodelReposit * * @return the created {@link SubmodelDescriptor} */ - public SubmodelDescriptor create() { + public SubmodelDescriptor create(Submodel submodel) { SubmodelDescriptor descriptor = new SubmodelDescriptor(); @@ -77,7 +74,7 @@ public SubmodelDescriptor create() { setIdShort(submodel.getIdShort(), descriptor); - setEndpointItem(submodel.getId(), descriptor); + setEndpointItem(submodel.getId(), descriptor, submodelRepositoryURLs); setDescription(submodel.getDescription(), descriptor); @@ -94,12 +91,7 @@ public SubmodelDescriptor create() { return descriptor; } - public SubmodelDescriptor create(Submodel submodel) { - this.submodel = submodel; - return create(); - } - - private void setDescription(List descriptions, SubmodelDescriptor descriptor) { + private static void setDescription(List descriptions, SubmodelDescriptor descriptor) { if (descriptions == null || descriptions.isEmpty()) return; @@ -107,7 +99,7 @@ private void setDescription(List descriptions, SubmodelDescr descriptor.setDescription(attributeMapper.mapDescription(descriptions)); } - private void setDisplayName(List displayNames, SubmodelDescriptor descriptor) { + private static void setDisplayName(List displayNames, SubmodelDescriptor descriptor) { if (displayNames == null || displayNames.isEmpty()) return; @@ -115,7 +107,7 @@ private void setDisplayName(List displayNames, SubmodelDescr descriptor.setDisplayName(attributeMapper.mapDisplayName(displayNames)); } - private void setExtensions(List extensions, SubmodelDescriptor descriptor) { + private static void setExtensions(List extensions, SubmodelDescriptor descriptor) { if (extensions == null || extensions.isEmpty()) return; @@ -123,7 +115,7 @@ private void setExtensions(List extensions, SubmodelDescriptor descri descriptor.setExtensions(attributeMapper.mapExtensions(extensions)); } - private void setAdministration(AdministrativeInformation administration, SubmodelDescriptor descriptor) { + private static void setAdministration(AdministrativeInformation administration, SubmodelDescriptor descriptor) { if (administration == null) return; @@ -131,7 +123,7 @@ private void setAdministration(AdministrativeInformation administration, Submode descriptor.setAdministration(attributeMapper.mapAdministration(administration)); } - private void setSemanticId(Reference reference, SubmodelDescriptor descriptor) { + private static void setSemanticId(Reference reference, SubmodelDescriptor descriptor) { if (reference == null) return; @@ -139,7 +131,7 @@ private void setSemanticId(Reference reference, SubmodelDescriptor descriptor) { descriptor.setSemanticId(attributeMapper.mapSemanticId(reference)); } - private void setSupplementalSemanticId(List supplementalSemanticIds, SubmodelDescriptor descriptor) { + private static void setSupplementalSemanticId(List supplementalSemanticIds, SubmodelDescriptor descriptor) { if (supplementalSemanticIds == null || supplementalSemanticIds.isEmpty()) return; @@ -147,7 +139,7 @@ private void setSupplementalSemanticId(List supplementalSemanticIds, descriptor.setSupplementalSemanticId(attributeMapper.mapSupplementalSemanticId(supplementalSemanticIds)); } - private void setEndpointItem(String shellId, SubmodelDescriptor descriptor) { + private static void setEndpointItem(String shellId, SubmodelDescriptor descriptor, List submodelRepositoryURLs) { for (String eachUrl : submodelRepositoryURLs) { Endpoint endpoint = new Endpoint(); @@ -159,7 +151,7 @@ private void setEndpointItem(String shellId, SubmodelDescriptor descriptor) { } } - private ProtocolInformation createProtocolInformation(String shellId, String url) { + private static ProtocolInformation createProtocolInformation(String shellId, String url) { String href = String.format("%s/%s", url, Base64UrlEncodedIdentifier.encodeIdentifier(shellId)); ProtocolInformation protocolInformation = new ProtocolInformation(); @@ -169,15 +161,15 @@ private ProtocolInformation createProtocolInformation(String shellId, String url return protocolInformation; } - private void setIdShort(String idShort, SubmodelDescriptor descriptor) { + private static void setIdShort(String idShort, SubmodelDescriptor descriptor) { descriptor.setIdShort(idShort); } - private void setId(String shellId, SubmodelDescriptor descriptor) { + private static void setId(String shellId, SubmodelDescriptor descriptor) { descriptor.setId(shellId); } - private String getProtocol(String endpoint) { + private static String getProtocol(String endpoint) { try { return new URL(endpoint).getProtocol(); } catch (MalformedURLException e) { @@ -185,7 +177,7 @@ private String getProtocol(String endpoint) { } } - private List createSubmodelRepositoryUrls(List submodelRepositoryBaseURLs) { + private static List createSubmodelRepositoryUrls(List submodelRepositoryBaseURLs) { List toReturn = new ArrayList<>(submodelRepositoryBaseURLs.size()); for (String eachUrl : submodelRepositoryBaseURLs) { toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, SUBMODEL_REPOSITORY_PATH)); diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java index 3b9638aed..ead8f6708 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java @@ -91,7 +91,7 @@ public void updateSubmodel(String submodelId, Submodel submodel) throws ElementD @Override public void createSubmodel(Submodel submodel) throws CollidingIdentifierException { - SubmodelDescriptor descriptor = new SubmodelDescriptorFactory(submodel, submodelRepositoryRegistryLink.getSubmodelRepositoryBaseURLs(), attributeMapper).create(); + SubmodelDescriptor descriptor = new SubmodelDescriptorFactory(submodelRepositoryRegistryLink.getSubmodelRepositoryBaseURLs(), attributeMapper).create(submodel); decorated.createSubmodel(submodel); diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java index a414b8108..f220de890 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java @@ -104,8 +104,8 @@ public void testExternalUrl() throws ApiException { private SubmodelDescriptor createAndRetrieveDescriptor(Submodel submodel) throws ApiException { registryIntegrationSubmodelRepository.createSubmodel(submodel); - SubmodelDescriptorFactory descriptorFactory = new SubmodelDescriptorFactory(submodel, mockedRegistryLink.getSubmodelRepositoryBaseURLs(), mockedAttributeMapper); - return descriptorFactory.create(); + SubmodelDescriptorFactory descriptorFactory = new SubmodelDescriptorFactory(mockedRegistryLink.getSubmodelRepositoryBaseURLs(), mockedAttributeMapper); + return descriptorFactory.create(submodel); } private Submodel createDummySubmodel() { diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java index ab232f2ab..87754a510 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java @@ -73,6 +73,8 @@ public class InMemorySubmodelService implements SubmodelService { private final FileRepository fileRepository; + private final Object submodelLock = new Object(); + /** * Creates the InMemory SubmodelService containing the passed Submodel * @@ -115,20 +117,22 @@ public SubmodelElementValue getSubmodelElementValue(String idShort) throws Eleme @SuppressWarnings("unchecked") @Override public void setSubmodelElementValue(String idShort, SubmodelElementValue value) throws ElementDoesNotExistException { - SubmodelElementValueMapperFactory submodelElementValueFactory = new SubmodelElementValueMapperFactory(); + synchronized (submodelLock) { + SubmodelElementValueMapperFactory submodelElementValueFactory = new SubmodelElementValueMapperFactory(); - ValueMapper valueMapper = submodelElementValueFactory.create(getSubmodelElement(idShort)); + ValueMapper valueMapper = submodelElementValueFactory.create(getSubmodelElement(idShort)); - valueMapper.setValue(value); + valueMapper.setValue(value); + } } @Override public void createSubmodelElement(SubmodelElement submodelElement) throws CollidingIdentifierException { - throwIfSubmodelElementExists(submodelElement.getIdShort()); - - List smElements = submodel.getSubmodelElements(); - smElements.add(submodelElement); - submodel.setSubmodelElements(smElements); + synchronized (submodelLock) { + List smElements = submodel.getSubmodelElements(); + throwIfSubmodelElementExists(submodelElement.getIdShort()); + smElements.add(submodelElement); + } } private void throwIfSubmodelElementExists(String submodelElementId) { @@ -142,46 +146,51 @@ private void throwIfSubmodelElementExists(String submodelElementId) { @Override public void createSubmodelElement(String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException, CollidingIdentifierException { - throwIfSubmodelElementExists(getFullIdShortPath(idShortPath, submodelElement.getIdShort())); - - SubmodelElement parentSme = parser.getSubmodelElementFromIdShortPath(idShortPath); - if (parentSme instanceof SubmodelElementList) { - SubmodelElementList list = (SubmodelElementList) parentSme; - List submodelElements = list.getValue(); - submodelElements.add(submodelElement); - list.setValue(submodelElements); - return; - } - if (parentSme instanceof SubmodelElementCollection) { - SubmodelElementCollection collection = (SubmodelElementCollection) parentSme; - List submodelElements = collection.getValue(); - submodelElements.add(submodelElement); - collection.setValue(submodelElements); - return; + synchronized (submodelLock) { + throwIfSubmodelElementExists(getFullIdShortPath(idShortPath, submodelElement.getIdShort())); + + SubmodelElement parentSme = parser.getSubmodelElementFromIdShortPath(idShortPath); + if (parentSme instanceof SubmodelElementList) { + SubmodelElementList list = (SubmodelElementList) parentSme; + List submodelElements = list.getValue(); + submodelElements.add(submodelElement); + list.setValue(submodelElements); + return; + } + if (parentSme instanceof SubmodelElementCollection) { + SubmodelElementCollection collection = (SubmodelElementCollection) parentSme; + List submodelElements = collection.getValue(); + submodelElements.add(submodelElement); + collection.setValue(submodelElements); + } } } @Override public void updateSubmodelElement(String idShortPath, SubmodelElement submodelElement) { - deleteSubmodelElement(idShortPath); + synchronized (submodelLock) { + deleteSubmodelElement(idShortPath); - String idShortPathParentSME = parser.getIdShortPathOfParentElement(idShortPath); - if (idShortPath.equals(idShortPathParentSME)) { - createSubmodelElement(submodelElement); - return; + String idShortPathParentSME = parser.getIdShortPathOfParentElement(idShortPath); + if (idShortPath.equals(idShortPathParentSME)) { + createSubmodelElement(submodelElement); + return; + } + createSubmodelElement(idShortPathParentSME, submodelElement); } - createSubmodelElement(idShortPathParentSME, submodelElement); } @Override public void deleteSubmodelElement(String idShortPath) throws ElementDoesNotExistException { - deleteAssociatedFileIfAny(idShortPath); + synchronized (submodelLock) { + deleteAssociatedFileIfAny(idShortPath); - if (!helper.isNestedIdShortPath(idShortPath)) { - deleteFlatSubmodelElement(idShortPath); - return; + if (!helper.isNestedIdShortPath(idShortPath)) { + deleteFlatSubmodelElement(idShortPath); + return; + } + deleteNestedSubmodelElement(idShortPath); } - deleteNestedSubmodelElement(idShortPath); } private void deleteNestedSubmodelElement(String idShortPath) { @@ -259,44 +268,48 @@ public java.io.File getFileByPath(String idShortPath) throws ElementDoesNotExist @Override public void setFileValue(String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException { - SubmodelElement submodelElement = getSubmodelElement(idShortPath); + synchronized (submodelLock) { + SubmodelElement submodelElement = getSubmodelElement(idShortPath); - throwIfSmElementIsNotAFile(submodelElement); + throwIfSmElementIsNotAFile(submodelElement); - File fileSmElement = (File) submodelElement; + File fileSmElement = (File) submodelElement; - if (fileRepository.exists(fileSmElement.getValue())) - fileRepository.delete(fileSmElement.getValue()); + if (fileRepository.exists(fileSmElement.getValue())) + fileRepository.delete(fileSmElement.getValue()); - String uniqueFileName = createUniqueFileName(idShortPath, fileName); + String uniqueFileName = createUniqueFileName(idShortPath, fileName); - FileMetadata fileMetadata = new FileMetadata(uniqueFileName, fileSmElement.getContentType(), inputStream); - - if(fileRepository.exists(fileMetadata.getFileName())) - fileRepository.delete(fileMetadata.getFileName()); + FileMetadata fileMetadata = new FileMetadata(uniqueFileName, fileSmElement.getContentType(), inputStream); - String filePath = fileRepository.save(fileMetadata); + if (fileRepository.exists(fileMetadata.getFileName())) + fileRepository.delete(fileMetadata.getFileName()); - FileBlobValue fileValue = new FileBlobValue(fileSmElement.getContentType(), filePath); + String filePath = fileRepository.save(fileMetadata); - setSubmodelElementValue(idShortPath, fileValue); + FileBlobValue fileValue = new FileBlobValue(fileSmElement.getContentType(), filePath); + + setSubmodelElementValue(idShortPath, fileValue); + } } @Override public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException { - SubmodelElement submodelElement = getSubmodelElement(idShortPath); + synchronized (submodelLock) { + SubmodelElement submodelElement = getSubmodelElement(idShortPath); - throwIfSmElementIsNotAFile(submodelElement); + throwIfSmElementIsNotAFile(submodelElement); - File fileSubmodelElement = (File) submodelElement; - String filePath = fileSubmodelElement.getValue(); + File fileSubmodelElement = (File) submodelElement; + String filePath = fileSubmodelElement.getValue(); - fileRepository.delete(filePath); + fileRepository.delete(filePath); - FileBlobValue fileValue = new FileBlobValue(" ", " "); + FileBlobValue fileValue = new FileBlobValue(" ", " "); - setSubmodelElementValue(idShortPath, fileValue); + setSubmodelElementValue(idShortPath, fileValue); + } } @Override From ee1f2713b3e3890aca5c1de05f10307acbf7fe35 Mon Sep 17 00:00:00 2001 From: Malte Hellmeier Date: Thu, 5 Dec 2024 17:30:08 +0100 Subject: [PATCH 08/17] fix: change lombok version for lombok-maven-plugin --- basyx.aasregistry/pom.xml | 15 +++++++++++++++ basyx.submodelregistry/pom.xml | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/basyx.aasregistry/pom.xml b/basyx.aasregistry/pom.xml index 6cf1072d5..ef83b9634 100644 --- a/basyx.aasregistry/pom.xml +++ b/basyx.aasregistry/pom.xml @@ -16,6 +16,7 @@ 1.18.20.0 + 1.18.30 ${java.version} ${java.version} UTF-8 @@ -75,6 +76,13 @@ org.projectlombok lombok-maven-plugin ${lombok.maven-plugin.version} + + + org.projectlombok + lombok + ${lombok.maven-plugin.lombok.version} + + org.apache.maven.plugins @@ -241,6 +249,13 @@ org.projectlombok lombok-maven-plugin + + + org.projectlombok + lombok + ${lombok.maven-plugin.lombok.version} + + generate-sources diff --git a/basyx.submodelregistry/pom.xml b/basyx.submodelregistry/pom.xml index 8cb518ecb..c4fc40aa9 100644 --- a/basyx.submodelregistry/pom.xml +++ b/basyx.submodelregistry/pom.xml @@ -16,6 +16,7 @@ 1.18.20.0 + 1.18.30 ${java.version} ${java.version} src/generated @@ -71,6 +72,13 @@ org.projectlombok lombok-maven-plugin ${lombok.maven-plugin.version} + + + org.projectlombok + lombok + ${lombok.maven-plugin.lombok.version} + + io.swagger.codegen.v3 @@ -222,6 +230,13 @@ org.projectlombok lombok-maven-plugin + + + org.projectlombok + lombok + ${lombok.maven-plugin.lombok.version} + + generate-sources From 2c68a75459b7529bf5033383e0c6bded696bb415 Mon Sep 17 00:00:00 2001 From: ShehriyarShariq-Fraunhofer Date: Fri, 6 Dec 2024 08:33:43 +0100 Subject: [PATCH 09/17] Implement Remaining MQTTSubmodelRepo Methods (#544) * stash * fix: remove sideeffects from all tests * fix: update serializeSubmodelElements method * fix: Update events in README * chore: add missing license header * fix: Update File Value events * ci: rerun * fix: patchSubmodelElements remove pagination --------- Co-authored-by: Mateus Molina --- .../serializer/SubmodelElementSerializer.java | 30 ++++ .../Readme.md | 3 + .../feature/mqtt/MqttSubmodelRepository.java | 106 +++++++++----- .../MqttSubmodelRepositoryTopicFactory.java | 32 +++++ .../mqtt/TestMqttSubmodelObserver.java | 129 ++++++++++++++++-- 5 files changed, 259 insertions(+), 41 deletions(-) diff --git a/basyx.common/basyx.mqttcore/src/main/java/org/eclipse/digitaltwin/basyx/common/mqttcore/serializer/SubmodelElementSerializer.java b/basyx.common/basyx.mqttcore/src/main/java/org/eclipse/digitaltwin/basyx/common/mqttcore/serializer/SubmodelElementSerializer.java index f2395f0f5..771fd9c43 100644 --- a/basyx.common/basyx.mqttcore/src/main/java/org/eclipse/digitaltwin/basyx/common/mqttcore/serializer/SubmodelElementSerializer.java +++ b/basyx.common/basyx.mqttcore/src/main/java/org/eclipse/digitaltwin/basyx/common/mqttcore/serializer/SubmodelElementSerializer.java @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.common.mqttcore.serializer; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; @@ -72,6 +74,34 @@ public static String serializeSubmodelElement(SubmodelElement submodelElement) { throw new RuntimeException(e); } } + + /** + * Serializer to create a JSON String for the given submodel elements. + * + * @param submodelElement + * @return serialized list of submodelElements as JSON String + */ + public static String serializeSubmodelElements(List submodelElements) { + try { + List updatedSubmodelElements = new ArrayList<>(); + + for(int i = 0; i < submodelElements.size(); i++) { + SubmodelElement elem = submodelElements.get(i); + SubmodelElement localElement; + if (shouldSendEmptyValueEvent(elem)) { + localElement = getSubmodelElementWithoutValue(elem); + } else { + localElement = elem; + } + + updatedSubmodelElements.add(localElement); + } + + return new JsonSerializer().writeList(updatedSubmodelElements); + } catch (SerializationException | DeserializationException e) { + throw new RuntimeException(e); + } + } /** * Generator to create a copy of a submodelElement without its value. diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/Readme.md b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/Readme.md index 628ebfdf5..fd810ae9c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/Readme.md +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/Readme.md @@ -9,5 +9,8 @@ This feature provides hierarchical MQTT eventing for a multitude of events: | SubmodelElement Created | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/$idShortPath/created | Created SubmodelElement JSON | | SubmodelElement Updated | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/$idShortPath/updated | Updated SubmodelElement JSON | | SubmodelElement Deleted | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/$idShortPath/deleted | Deleted SubmodelElement JSON | +| SubmodelElements Patched | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/patched | Patched SubmodelElements JSON | +| FileValue Updated | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/$idShortPath/attachment/updated | Updated SubmodelElement JSON | +| FileValue Deleted | sm-repository/$repoId/submodels/$submodelIdBase64URLEncoded/submodelElements/$idShortPath/attachment/deleted | Deleted SubmodelElement JSON | Per default, the SubmodelElement topic payloads include the SubmodelElement's value. If this is not desired, the SubmodelElement can be annotated with a Qualifier of type *emptyValueUpdateEvent* and value *true* diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java index 86d11bc29..9b4e661be 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java @@ -1,5 +1,31 @@ +/******************************************************************************* + * 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.submodelrepository.feature.mqtt; +import java.io.File; import java.io.InputStream; import java.util.List; @@ -126,6 +152,12 @@ public void deleteSubmodelElement(String submodelId, String idShortPath) throws submodelElementDeleted(submodelElement, getName(), submodelId, idShortPath); } + @Override + public void patchSubmodelElements(String submodelId, List submodelElementList) { + decorated.patchSubmodelElements(submodelId, submodelElementList); + submodelElementsPatched(submodelElementList, getName(), submodelId); + } + @Override public String getName() { return decorated.getName(); @@ -141,6 +173,35 @@ public Submodel getSubmodelByIdMetadata(String submodelId) { return decorated.getSubmodelByIdMetadata(submodelId); } + @Override + public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException { + return decorated.invokeOperation(submodelId, idShortPath, input); + } + + @Override + public java.io.File getFileByPathSubmodel(String submodelId, String idShortPath) { + return decorated.getFileByPathSubmodel(submodelId, idShortPath); + } + + @Override + public void deleteFileValue(String identifier, String idShortPath) { + SubmodelElement submodelElement = decorated.getSubmodelElement(identifier, idShortPath); + decorated.deleteFileValue(identifier, idShortPath); + fileValueDeleted(submodelElement, getName(), identifier, idShortPath); + } + + @Override + public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream){ + decorated.setFileValue(submodelId, idShortPath, fileName, inputStream); + SubmodelElement submodelElement = decorated.getSubmodelElement(submodelId, idShortPath); + fileValueUpdated(submodelElement, getName(), submodelId, idShortPath); + } + + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return decorated.getFileByFilePath(submodelId, filePath); + } + private void submodelCreated(Submodel submodel, String repoId) { sendMqttMessage(topicFactory.createCreateSubmodelTopic(repoId), SubmodelSerializer.serializeSubmodel(submodel)); } @@ -164,6 +225,18 @@ private void submodelElementUpdated(SubmodelElement submodelElement, String repo private void submodelElementDeleted(SubmodelElement submodelElement, String repoId, String submodelId, String submodelElementId) { sendMqttMessage(topicFactory.createDeleteSubmodelElementTopic(repoId, submodelId, submodelElementId), SubmodelElementSerializer.serializeSubmodelElement(submodelElement)); } + + private void submodelElementsPatched(List submodelElements, String repoId, String submodelId) { + sendMqttMessage(topicFactory.createPatchSubmodelElementsTopic(repoId, submodelId), SubmodelElementSerializer.serializeSubmodelElements(submodelElements)); + } + + private void fileValueDeleted(SubmodelElement submodelElement, String repoId, String submodelId, String submodelElementId) { + sendMqttMessage(topicFactory.createDeleteFileValueTopic(repoId, submodelId, submodelElementId), SubmodelElementSerializer.serializeSubmodelElement(submodelElement)); + } + + private void fileValueUpdated(SubmodelElement submodelElement, String repoId, String submodelId, String submodelElementId) { + sendMqttMessage(topicFactory.createUpdateFileValueTopic(repoId, submodelId, submodelElementId), SubmodelElementSerializer.serializeSubmodelElement(submodelElement)); + } /** * Sends MQTT message to connected broker @@ -194,37 +267,4 @@ private MqttMessage createMqttMessage(String payload) { } } - @Override - public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException { - return decorated.invokeOperation(submodelId, idShortPath, input); - } - - @Override - public java.io.File getFileByPathSubmodel(String submodelId, String idShortPath) { - return decorated.getFileByPathSubmodel(submodelId, idShortPath); - } - - @Override - public void deleteFileValue(String identifier, String idShortPath) { - // TODO: Eventing - decorated.deleteFileValue(identifier, idShortPath); - } - - @Override - public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream){ - // TODO: Eventing - decorated.setFileValue(submodelId, idShortPath, fileName, inputStream); - } - - @Override - public void patchSubmodelElements(String submodelId, List submodelElementList) { - // TODO: Eventing - decorated.patchSubmodelElements(submodelId, submodelElementList); - } - - @Override - public InputStream getFileByFilePath(String submodelId, String filePath) { - return decorated.getFileByFilePath(submodelId, filePath); - } - } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepositoryTopicFactory.java b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepositoryTopicFactory.java index ea4d29263..14ab1f0e5 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepositoryTopicFactory.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepositoryTopicFactory.java @@ -41,7 +41,9 @@ public class MqttSubmodelRepositoryTopicFactory extends AbstractMqttTopicFactory private static final String CREATED = "created"; private static final String UPDATED = "updated"; private static final String DELETED = "deleted"; + private static final String PATCHED = "patched"; private static final String SUBMODELELEMENTS = "submodelElements"; + private static final String ATTACHMENT = "attachment"; /** * @param encoder @@ -104,4 +106,34 @@ public String createUpdateSubmodelElementTopic(String repoId, String submodelId, public String createDeleteSubmodelElementTopic(String repoId, String submodelId, String submodelElementId) { return new StringJoiner("/", "", "").add(SUBMODELREPOSITORY).add(repoId).add(SUBMODELS).add(encodeId(submodelId)).add(SUBMODELELEMENTS).add(submodelElementId).add(DELETED).toString(); } + + /** + * Creates the hierarchical topic for the patch event of submodelElements + * + * @param repoId + * @param submodelId + */ + public String createPatchSubmodelElementsTopic(String repoId, String submodelId) { + return new StringJoiner("/", "", "").add(SUBMODELREPOSITORY).add(repoId).add(SUBMODELS).add(encodeId(submodelId)).add(SUBMODELELEMENTS).add(PATCHED).toString(); + } + + /** + * Creates the hierarchical topic for the delete event of a file of a file element + * + * @param repoId + * + */ + public String createDeleteFileValueTopic(String repoId, String submodelId, String submodelElementId) { + return new StringJoiner("/", "", "").add(SUBMODELREPOSITORY).add(repoId).add(SUBMODELS).add(encodeId(submodelId)).add(SUBMODELELEMENTS).add(submodelElementId).add(ATTACHMENT).add(DELETED).toString(); + } + + /** + * Creates the hierarchical topic for the update event of a file of a file element + * + * @param repoId + * + */ + public String createUpdateFileValueTopic(String repoId, String submodelId, String submodelElementId) { + return new StringJoiner("/", "", "").add(SUBMODELREPOSITORY).add(repoId).add(SUBMODELS).add(encodeId(submodelId)).add(SUBMODELELEMENTS).add(submodelElementId).add(ATTACHMENT).add(UPDATED).toString(); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java index 1198e9ab1..2afc466d0 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java @@ -28,21 +28,30 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer; +import org.eclipse.digitaltwin.aas4j.v3.model.File; import org.eclipse.digitaltwin.aas4j.v3.model.Property; import org.eclipse.digitaltwin.aas4j.v3.model.Qualifier; import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultFile; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultQualifier; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; import org.eclipse.digitaltwin.basyx.common.mqttcore.encoding.Base64URLEncoder; import org.eclipse.digitaltwin.basyx.common.mqttcore.serializer.SubmodelElementSerializer; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileMetadata; +import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository; import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; @@ -58,6 +67,9 @@ import org.junit.BeforeClass; import org.junit.Test; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + import io.moquette.broker.Server; import io.moquette.broker.config.ClasspathResourceLoader; import io.moquette.broker.config.IConfig; @@ -74,7 +86,13 @@ public class TestMqttSubmodelObserver { private static MqttSubmodelRepositoryTopicFactory topicFactory = new MqttSubmodelRepositoryTopicFactory(new Base64URLEncoder()); private static SubmodelRepository submodelRepository; + + private static JsonDeserializer deserializer = new JsonDeserializer(); + private static final String FILE_SUBMODEL_ELEMENT_NAME = "testFile.txt"; + private static final String FILE_SUBMODEL_ELEMENT_CONTENT = "This is a text file."; + private static String SAVED_FILE_PATH = ""; + @BeforeClass public static void setUpClass() throws MqttException, IOException { mqttBroker = startBroker(); @@ -135,10 +153,10 @@ public void createSubmodelElementEvent() throws DeserializationException { @Test public void updateSubmodelElementEvent() throws DeserializationException { - Submodel submodel = createSubmodelDummy("updateSubmodelForElementEventId"); + Submodel submodel = createSubmodelDummyWithSubmodelElement("updateSubmodelForElementEventId", "updateSubmodelElementEventId"); submodelRepository.createSubmodel(submodel); - SubmodelElement submodelElement = createSubmodelElementDummy("updateSubmodelElementEventId"); - submodelRepository.createSubmodelElement(submodel.getId(), submodelElement); + SubmodelElement submodelElement = submodel.getSubmodelElements().get(0); + SubmodelElementValue value = new PropertyValue("updatedValue"); submodelRepository.setSubmodelElementValue(submodel.getId(), submodelElement.getIdShort(), value); @@ -148,10 +166,11 @@ public void updateSubmodelElementEvent() throws DeserializationException { @Test public void deleteSubmodelElementEvent() throws DeserializationException { - Submodel submodel = createSubmodelDummy("deleteSubmodelForElementEventId"); + Submodel submodel = createSubmodelDummyWithSubmodelElement("deleteSubmodelForElementEventId", "deleteSubmodelElementEventId"); submodelRepository.createSubmodel(submodel); - SubmodelElement submodelElement = createSubmodelElementDummy("deleteSubmodelElementEventId"); - submodelRepository.createSubmodelElement(submodel.getId(), submodelElement); + + SubmodelElement submodelElement = submodel.getSubmodelElements().get(0); + submodelRepository.deleteSubmodelElement(submodel.getId(), submodelElement.getIdShort()); assertEquals(topicFactory.createDeleteSubmodelElementTopic(submodelRepository.getName(), submodel.getId(), submodelElement.getIdShort()), listener.lastTopic); @@ -175,6 +194,50 @@ public void createSubmodelElementWithoutValueEvent() throws DeserializationExcep assertEquals(submodelElement, deserializeSubmodelElementPayload(listener.lastPayload)); } + @Test + public void patchSubmodelElementsEvent() throws DeserializationException, JsonMappingException, JsonProcessingException { + Submodel submodel = createSubmodelDummyWithSubmodelElements("patchSubmodelForElementEventId"); + submodelRepository.createSubmodel(submodel); + + List submodelElements = submodel.getSubmodelElements(); + + for (int i = 0; i < submodelElements.size(); i++) { + SubmodelElement submodelElement = submodelElements.get(i); + submodelElement.setIdShort("patchedSubmodelElementId_" + i); + } + + submodelRepository.patchSubmodelElements(submodel.getId(), submodelElements); + + assertEquals(topicFactory.createPatchSubmodelElementsTopic(submodelRepository.getName(), submodel.getId()), listener.lastTopic); + assertEquals(submodelElements, deserializeSubmodelElementsListPayload(listener.lastPayload)); + } + + @Test + public void setFileValueEvent() throws DeserializationException, IOException { + Submodel submodel = createSubmodelDummyWithFileSubmodelElement("setSubmodelFileValueEventId", "setFileValueSubmodelElementEventId"); + submodelRepository.createSubmodel(submodel); + + File submodelElement = (File) submodel.getSubmodelElements().get(0); + + submodelRepository.setFileValue(submodel.getId(), submodelElement.getIdShort(), FILE_SUBMODEL_ELEMENT_NAME, getInputStreamOfDummyFile(FILE_SUBMODEL_ELEMENT_CONTENT)); + + assertEquals(topicFactory.createUpdateFileValueTopic(submodelRepository.getName(), submodel.getId(), submodelElement.getIdShort()), listener.lastTopic); + assertEquals(submodelElement, deserializeSubmodelElementPayload(listener.lastPayload)); + } + + @Test + public void deleteFileValueEvent() throws DeserializationException, IOException { + Submodel submodel = createSubmodelDummyWithFileSubmodelElement("deleteSubmodelFileValueEventId", "deleteFileValueSubmodelElementEventId"); + submodelRepository.createSubmodel(submodel); + + File submodelElement = (File) submodel.getSubmodelElements().get(0); + + submodelRepository.deleteFileValue(submodel.getId(), submodelElement.getIdShort()); + + assertEquals(topicFactory.createDeleteFileValueTopic(submodelRepository.getName(), submodel.getId(), submodelElement.getIdShort()), listener.lastTopic); + assertEquals(submodelElement, deserializeSubmodelElementPayload(listener.lastPayload)); + } + private List createNoValueQualifierList() { Qualifier emptyValueQualifier = new DefaultQualifier.Builder().type(SubmodelElementSerializer.EMPTYVALUEUPDATE_TYPE).value("true").build(); return Arrays.asList(emptyValueQualifier); @@ -187,17 +250,67 @@ private Submodel deserializeSubmodelPayload(String payload) throws Deserializati private SubmodelElement deserializeSubmodelElementPayload(String payload) throws DeserializationException { return new JsonDeserializer().read(payload, SubmodelElement.class); } + + private List deserializeSubmodelElementsListPayload(String payload) throws DeserializationException, JsonMappingException, JsonProcessingException { + return deserializer.readList(payload, SubmodelElement.class); + } private Submodel createSubmodelDummy(String submodelId) { return new DefaultSubmodel.Builder().id(submodelId).build(); } + + private Submodel createSubmodelDummyWithSubmodelElement(String submodelId, String submodelElementId) { + List submodelElements = new ArrayList<>(); + + submodelElements.add(createSubmodelElementDummy(submodelElementId)); + + return new DefaultSubmodel.Builder().id(submodelId).submodelElements(submodelElements).build(); + } + + private Submodel createSubmodelDummyWithFileSubmodelElement(String submodelId, String submodelElementId) { + List submodelElements = new ArrayList<>(); + + submodelElements.add(createFileSubmodelElement(submodelElementId)); + + return new DefaultSubmodel.Builder().id(submodelId).submodelElements(submodelElements).build(); + } + + private Submodel createSubmodelDummyWithSubmodelElements(String submodelId) { + List submodelElements = createSubmodelElementsListDummy(2); + + return new DefaultSubmodel.Builder().id(submodelId).submodelElements(submodelElements).build(); + } private SubmodelElement createSubmodelElementDummy(String submodelElementId) { + Property defaultProp = new DefaultProperty.Builder().idShort(submodelElementId).value("defaultValue").build(); + return new DefaultProperty.Builder().idShort(submodelElementId).value("defaultValue").build(); } + + public File createFileSubmodelElement(String submodelElementId) { + return new DefaultFile.Builder().idShort(submodelElementId).value(SAVED_FILE_PATH).contentType("text/plain").build(); + } + + private static InputStream getInputStreamOfDummyFile(String fileContent) throws FileNotFoundException, IOException { + return new ByteArrayInputStream(fileContent.getBytes()); + } + + private List createSubmodelElementsListDummy(int count) { + List submodelElements = new ArrayList(); + + for (int i = 0; i < count; i++) { + submodelElements.add(createSubmodelElementDummy("submodelElementId_" + i)); + } + + return submodelElements; + } - private static SubmodelRepository createMqttSubmodelRepository(MqttClient client) { - SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())); + private static SubmodelRepository createMqttSubmodelRepository(MqttClient client) throws FileHandlingException, FileNotFoundException, IOException { + FileRepository fileRepository = new InMemoryFileRepository(); + + SAVED_FILE_PATH = fileRepository.save(new FileMetadata(FILE_SUBMODEL_ELEMENT_NAME, "", getInputStreamOfDummyFile(FILE_SUBMODEL_ELEMENT_CONTENT))); + + SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(fileRepository)); return new MqttSubmodelRepositoryFactory(repoFactory, client, new MqttSubmodelRepositoryTopicFactory(new Base64URLEncoder())).create(); } From 42be9714d06c2a2cb42b8dc0929be9731e854223 Mon Sep 17 00:00:00 2001 From: Mateus Molina Date: Fri, 6 Dec 2024 16:51:59 +0100 Subject: [PATCH 10/17] fix: clientExample not running in windows builds (#561) --- examples/BaSyxClient/config/erp.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/BaSyxClient/config/erp.env b/examples/BaSyxClient/config/erp.env index 50f5409ab..5e4b725fa 100644 --- a/examples/BaSyxClient/config/erp.env +++ b/examples/BaSyxClient/config/erp.env @@ -1 +1 @@ -ERP_GEN_INTERVAL=10 # Generate a new CSV every 10 seconds \ No newline at end of file +ERP_GEN_INTERVAL=10 \ No newline at end of file From 9afa5adc5ef41c154efa9002193d4386297e79f8 Mon Sep 17 00:00:00 2001 From: Aaron Zielstorff Date: Fri, 6 Dec 2024 23:29:04 +0100 Subject: [PATCH 11/17] Adds GitHub action to automatically push snapshot images to dockerhub (#562) * Adds workflow for automatic push of snapshot images on PR merge * adds personal repo for push testing * fix the content path * removes build args * fixes file path * Adds registries to docker push action * fixes registry pathes * Adds final name as build arg * adds fix to determine final name * adds fix to final name * adds fix for registry jar file location * fix the modules root * updates dockerfiles * fixes path to registry jar * applies another fix to jar path * adss docker namespace variable --- .github/workflows/docker-snapshot-release.yml | 138 ++++++++++++++++++ .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- .../src/main/docker/Dockerfile | 5 +- 9 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/docker-snapshot-release.yml diff --git a/.github/workflows/docker-snapshot-release.yml b/.github/workflows/docker-snapshot-release.yml new file mode 100644 index 000000000..1428e5f82 --- /dev/null +++ b/.github/workflows/docker-snapshot-release.yml @@ -0,0 +1,138 @@ +name: Build and Push Docker Images on PR Merge + +on: + push: + branches: + - main + paths-ignore: + - '.github/ISSUE_TEMPLATE/**' + - '.github/CODE_OF_CONDUCT.md' + - '.github/CODING_CONVENTIONS.md' + - '.github/CONTRIBUTING.md' + - '.github/dependabot.yml' + - '.github/pull_request_template.md' + - '.github/SECURITY.md' + - 'docs/**' + - 'examples/**' + - 'README.md' + - '.gitattributes' + - '.gitignore' + - 'LICENSE' + - 'NOTICE' + +env: + DOCKER_NAMESPACE: eclipsebasyx + +jobs: + build-and-push-prerelease: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: aas-environment + path: basyx.aasenvironment/basyx.aasenvironment.component + - name: aas-repository + path: basyx.aasrepository/basyx.aasrepository.component + - name: submodel-repository + path: basyx.submodelrepository/basyx.submodelrepository.component + - name: conceptdescription-repository + path: basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository.component + - name: aas-discovery + path: basyx.aasdiscoveryservice/basyx.aasdiscoveryservice.component + - name: aasxfileserver + path: basyx.aasxfileserver/basyx.aasxfileserver.component + - name: aas-registry-kafka-mem + path: basyx.aasregistry/basyx.aasregistry-service-release-kafka-mem/src/main/docker + - name: aas-registry-kafka-mongodb + path: basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/main/docker + - name: aas-registry-log-mem + path: basyx.aasregistry/basyx.aasregistry-service-release-log-mem/src/main/docker + - name: aas-registry-log-mongodb + path: basyx.aasregistry/basyx.aasregistry-service-release-log-mongodb/src/main/docker + - name: submodel-registry-kafka-mem + path: basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mem/src/main/docker + - name: submodel-registry-kafka-mongodb + path: basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/main/docker + - name: submodel-registry-log-mem + path: basyx.submodelregistry/basyx.submodelregistry-service-release-log-mem/src/main/docker + - name: submodel-registry-log-mongodb + path: basyx.submodelregistry/basyx.submodelregistry-service-release-log-mongodb/src/main/docker + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Build the project + # For registry modules, we activate the dockerbuild profile and specify the module with --pl + - name: Build BaSyx + run: | + if [[ "${{ matrix.name }}" == *"registry"* ]]; then + # Derive the module's artifactId from the path + module_root=$(dirname "$(dirname "$(dirname "${{ matrix.path }}")")") + artifact_id=$(basename "$module_root") + # Run with dockerbuild profile and namespace + mvn clean install -DskipTests -Pdockerbuild "-Ddocker.namespace=${{ env.DOCKER_NAMESPACE }}" --pl "org.eclipse.digitaltwin.basyx:${artifact_id}" + else + mvn clean install -DskipTests + fi + + - name: Prepare Registry JAR for Docker + if: contains(matrix.name, 'registry') + run: | + # Go three levels up from src/main/docker to get the module root + module_root=$(dirname "$(dirname "$(dirname "${{ matrix.path }}")")") + + # Adjust the path to where the dockerbuild profile places the JAR + JAR_FILE=$(ls "$module_root/target/docker/${{ env.DOCKER_NAMESPACE }}/${{ matrix.name }}/2.0.0-SNAPSHOT/build/maven/"*.jar | head -n 1) + if [ -z "$JAR_FILE" ]; then + echo "No repackaged JAR found in $module_root/target/docker/${{ env.DOCKER_NAMESPACE }}/${{ matrix.name }}/2.0.0-SNAPSHOT/build/maven. Check your build." + exit 1 + fi + + # Create the maven directory inside the Docker context and copy the JAR there + mkdir -p "${{ matrix.path }}/maven" + cp "$JAR_FILE" "${{ matrix.path }}/maven/" + + # Extract the final name without .jar extension + FINAL_NAME=$(basename "$JAR_FILE" .jar) + echo "FINAL_ARGS=FINAL_NAME=${FINAL_NAME}" >> $GITHUB_ENV + + - name: No-Op for Non-Registry Modules + if: "!contains(matrix.name, 'registry')" + run: echo "FINAL_ARGS=" >> $GITHUB_ENV + + - name: Build and Push Docker Image + uses: docker/build-push-action@v6 + with: + context: ${{ matrix.path }} + file: ${{ matrix.path }}/Dockerfile + push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7 + tags: | + ${{ env.DOCKER_NAMESPACE }}/${{ matrix.name }}:2.0.0-SNAPSHOT + build-args: ${{ env.FINAL_ARGS }} + + - name: Verify Docker Image + run: | + docker pull ${{ env.DOCKER_NAMESPACE }}/${{ matrix.name }}:2.0.0-SNAPSHOT diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mem/src/main/docker/Dockerfile b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mem/src/main/docker/Dockerfile index bbddc1da9..a5cb3a888 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mem/src/main/docker/Dockerfile +++ b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mem/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile index 7e157b1ca..d408d753e 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile +++ b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-log-mem/src/main/docker/Dockerfile b/basyx.aasregistry/basyx.aasregistry-service-release-log-mem/src/main/docker/Dockerfile index d2f36e7ad..8ba0cf83a 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-release-log-mem/src/main/docker/Dockerfile +++ b/basyx.aasregistry/basyx.aasregistry-service-release-log-mem/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-log-mongodb/src/main/docker/Dockerfile b/basyx.aasregistry/basyx.aasregistry-service-release-log-mongodb/src/main/docker/Dockerfile index 3acf47076..df632dacc 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-release-log-mongodb/src/main/docker/Dockerfile +++ b/basyx.aasregistry/basyx.aasregistry-service-release-log-mongodb/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mem/src/main/docker/Dockerfile b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mem/src/main/docker/Dockerfile index bbddc1da9..a5cb3a888 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mem/src/main/docker/Dockerfile +++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mem/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile index 7e157b1ca..d408d753e 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile +++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mem/src/main/docker/Dockerfile b/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mem/src/main/docker/Dockerfile index d2f36e7ad..8ba0cf83a 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mem/src/main/docker/Dockerfile +++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mem/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mongodb/src/main/docker/Dockerfile b/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mongodb/src/main/docker/Dockerfile index 3acf47076..df632dacc 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mongodb/src/main/docker/Dockerfile +++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-log-mongodb/src/main/docker/Dockerfile @@ -1,6 +1,7 @@ FROM eclipse-temurin:17 as builder -COPY maven/${project.build.finalName}.jar ./ -RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract +ARG FINAL_NAME=${project.build.finalName} +COPY maven/${FINAL_NAME}.jar ./ +RUN java -Djarmode=layertools -jar ${FINAL_NAME}.jar extract FROM eclipse-temurin:17 RUN mkdir /workspace From 8c5d518f5ea1234be3b9c977ffc8d29273a56442 Mon Sep 17 00:00:00 2001 From: Aaron Zielstorff Date: Fri, 6 Dec 2024 23:42:42 +0100 Subject: [PATCH 12/17] Update docker-snapshot-release.yml Include -am (also make) flag to ensure that dependencies are build first --- .github/workflows/docker-snapshot-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-snapshot-release.yml b/.github/workflows/docker-snapshot-release.yml index 1428e5f82..918fd4833 100644 --- a/.github/workflows/docker-snapshot-release.yml +++ b/.github/workflows/docker-snapshot-release.yml @@ -92,7 +92,7 @@ jobs: module_root=$(dirname "$(dirname "$(dirname "${{ matrix.path }}")")") artifact_id=$(basename "$module_root") # Run with dockerbuild profile and namespace - mvn clean install -DskipTests -Pdockerbuild "-Ddocker.namespace=${{ env.DOCKER_NAMESPACE }}" --pl "org.eclipse.digitaltwin.basyx:${artifact_id}" + mvn clean install -DskipTests -Pdockerbuild "-Ddocker.namespace=${{ env.DOCKER_NAMESPACE }}" --pl "org.eclipse.digitaltwin.basyx:${artifact_id}" -am else mvn clean install -DskipTests fi From 31ad48132a45d74174f7d9c580025472389795ef Mon Sep 17 00:00:00 2001 From: Aaron Zielstorff Date: Fri, 6 Dec 2024 23:48:51 +0100 Subject: [PATCH 13/17] Update docker-snapshot-release.yml Adds step to build all modules first --- .github/workflows/docker-snapshot-release.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-snapshot-release.yml b/.github/workflows/docker-snapshot-release.yml index 918fd4833..f6d5f0798 100644 --- a/.github/workflows/docker-snapshot-release.yml +++ b/.github/workflows/docker-snapshot-release.yml @@ -83,6 +83,9 @@ jobs: distribution: 'temurin' cache: maven + - name: Build all modules first + run: mvn clean install -DskipTests + # Build the project # For registry modules, we activate the dockerbuild profile and specify the module with --pl - name: Build BaSyx @@ -92,9 +95,9 @@ jobs: module_root=$(dirname "$(dirname "$(dirname "${{ matrix.path }}")")") artifact_id=$(basename "$module_root") # Run with dockerbuild profile and namespace - mvn clean install -DskipTests -Pdockerbuild "-Ddocker.namespace=${{ env.DOCKER_NAMESPACE }}" --pl "org.eclipse.digitaltwin.basyx:${artifact_id}" -am + mvn clean install -DskipTests -Pdockerbuild "-Ddocker.namespace=${{ env.DOCKER_NAMESPACE }}" --pl "org.eclipse.digitaltwin.basyx:${artifact_id}" else - mvn clean install -DskipTests + echo "Non-registry module - already built in the previous step." fi - name: Prepare Registry JAR for Docker From 57695ca70739a2b37d035e5e54885d24e4ee6119 Mon Sep 17 00:00:00 2001 From: Mateus Molina Date: Fri, 6 Dec 2024 23:53:55 +0100 Subject: [PATCH 14/17] fix: add up-to-date regex in patch-base-extensions (#556) --- basyx.aasregistry/open-api/patch-base-extensions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basyx.aasregistry/open-api/patch-base-extensions.yaml b/basyx.aasregistry/open-api/patch-base-extensions.yaml index 37082ee38..a6ca0bb6f 100644 --- a/basyx.aasregistry/open-api/patch-base-extensions.yaml +++ b/basyx.aasregistry/open-api/patch-base-extensions.yaml @@ -92,7 +92,7 @@ value: "^(0|[1-9][0-9]{0,3})$" - op: replace path: /components/schemas/AdministrativeInformation/allOf/1/properties/templateId/pattern - value: "^([\\t\\n\\r \\ud7ff\\ue000-\\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$" + value: "^([\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$" ## EmbeddedDataSpecification - op: replace path: /components/schemas/EmbeddedDataSpecification/properties/dataSpecificationContent/$ref From 795da16986984579e15d77fccb7e48d7d4f88a76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 00:40:26 +0100 Subject: [PATCH 15/17] Bump commons-io:commons-io from 2.17.0 to 2.18.0 (#550) Bumps commons-io:commons-io from 2.17.0 to 2.18.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36dd3ae3b..2b32c14eb 100644 --- a/pom.xml +++ b/pom.xml @@ -395,7 +395,7 @@ commons-io commons-io - 2.17.0 + 2.18.0 From be03c1e167bf912c3520f828f817d75cf1b55daa Mon Sep 17 00:00:00 2001 From: Sebastian Eicke Date: Sat, 7 Dec 2024 00:44:11 +0100 Subject: [PATCH 16/17] Update aas-gui images in example docker compose files (#553) --- examples/BaSyxClient/docker-compose.yml | 2 +- examples/BaSyxDatabridge/docker-compose.yml | 2 +- examples/BaSyxMinimal/docker-compose.yml | 2 +- examples/BaSyxNGINX/docker-compose.yml | 2 +- examples/BaSyxOperationDelegation/docker-compose.yml | 2 +- examples/BaSyxSecured/docker-compose.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/BaSyxClient/docker-compose.yml b/examples/BaSyxClient/docker-compose.yml index f44017df9..5fcf076c9 100644 --- a/examples/BaSyxClient/docker-compose.yml +++ b/examples/BaSyxClient/docker-compose.yml @@ -30,7 +30,7 @@ services: - ./config/sm-registry.yml:/workspace/config/application.yml aas-web-ui: - image: eclipsebasyx/aas-gui:v2-240913 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-ui ports: - '3000:3000' diff --git a/examples/BaSyxDatabridge/docker-compose.yml b/examples/BaSyxDatabridge/docker-compose.yml index 01a180222..e04089f52 100644 --- a/examples/BaSyxDatabridge/docker-compose.yml +++ b/examples/BaSyxDatabridge/docker-compose.yml @@ -37,7 +37,7 @@ services: # AAS Web UI aas-web-ui: - image: eclipsebasyx/aas-gui:v2-241006 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-ui ports: - '3000:3000' diff --git a/examples/BaSyxMinimal/docker-compose.yml b/examples/BaSyxMinimal/docker-compose.yml index ffa899d73..844ed1498 100644 --- a/examples/BaSyxMinimal/docker-compose.yml +++ b/examples/BaSyxMinimal/docker-compose.yml @@ -80,7 +80,7 @@ services: condition: service_healthy aas-web-ui_v2: - image: eclipsebasyx/aas-gui:v2-240801 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-web-ui_v2 ports: - "3000:3000" diff --git a/examples/BaSyxNGINX/docker-compose.yml b/examples/BaSyxNGINX/docker-compose.yml index caa8e67c1..07a4179eb 100644 --- a/examples/BaSyxNGINX/docker-compose.yml +++ b/examples/BaSyxNGINX/docker-compose.yml @@ -76,7 +76,7 @@ services: # AAS Web UI aas-web-ui: - image: eclipsebasyx/aas-gui:v2-240801 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-ui ports: - "3000:3000" diff --git a/examples/BaSyxOperationDelegation/docker-compose.yml b/examples/BaSyxOperationDelegation/docker-compose.yml index 146ec035c..7c4e33799 100644 --- a/examples/BaSyxOperationDelegation/docker-compose.yml +++ b/examples/BaSyxOperationDelegation/docker-compose.yml @@ -31,7 +31,7 @@ services: - ./basyx/sm-registry.yml:/workspace/config/application.yml restart: always aas-web-ui: - image: eclipsebasyx/aas-gui:v2-241114 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-ui ports: - '3000:3000' diff --git a/examples/BaSyxSecured/docker-compose.yaml b/examples/BaSyxSecured/docker-compose.yaml index 458a71e35..1fd3f1e2e 100644 --- a/examples/BaSyxSecured/docker-compose.yaml +++ b/examples/BaSyxSecured/docker-compose.yaml @@ -75,7 +75,7 @@ services: # AAS Web UI aas-web-ui: - image: eclipsebasyx/aas-gui:v2-240801 + image: eclipsebasyx/aas-gui:SNAPSHOT container_name: aas-ui extra_hosts: - "keycloak:127.0.0.1" From f51da9ecf199ad0f2e8cab857ad644097256079f Mon Sep 17 00:00:00 2001 From: Aaron Zielstorff Date: Sat, 7 Dec 2024 09:00:14 +0100 Subject: [PATCH 17/17] Adds link to time series data example --- examples/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index a9d44aa1b..9382a2099 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,6 +4,10 @@ This folder contains example configurations and docker compose files for setting ## Standalone Submodel For definining standalone submodels, see [Submodel Service](../basyx.submodelservice) +## Time Series Data Submodel Example +An example showcasing the integration of time series data leveraging the plugin in the AAS Web UI. +See [Time Series Data Example](https://github.com/eclipse-basyx/basyx-aas-web-ui/tree/main/examples/TimeSeriesData) in the repository of the AAS Web UI. + ## BaSyx with NGINX See the separate [NGINX example](BaSyxNGINX) for a comprehensive setup leveraging NGINX. @@ -17,4 +21,4 @@ See the separate [Operation Delegation Example](BaSyxOperationDelegation) for a See the separate [Secure Setup Example](BaSyxSecured) for a comprehensive setup leveraging Keycloak. ## BaSyx Client Example -See the separate [Client Example](BaSyxClient) for a comprehensive setup leveraging the BaSyx Java Client library. \ No newline at end of file +See the separate [Client Example](BaSyxClient) for a comprehensive setup leveraging the BaSyx Java Client library.