Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add feature hierarchical registry for AAS and Submodel Registries #549

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0419e6b
wip: Add a registry hierarchy feature module to AAS Registry
mateusmolina-iese Jun 3, 2024
579ec76
wip: Revert ci compose change
mateusmolina-iese Jun 3, 2024
188e9a0
wip: Rename package
mateusmolina-iese Jun 3, 2024
bd776ea
wip: Initial Test Setup
mateusmolina-iese Jun 3, 2024
ae02819
wip: Implement tests for Hierarchal AasRegistry
mateusmolina-iese Jun 3, 2024
60fcc12
Implement getAasDescriptor hierarchal method
mateusmolina-iese Jun 3, 2024
951ba23
Implement hierarchal getSubmodelDescriptor
mateusmolina-iese Jun 4, 2024
ede6f7f
Extract ApiExpectionMappers to helper class
mateusmolina-iese Jun 4, 2024
e0c8a89
Extract delegation strategy to own component
mateusmolina-iese Jun 4, 2024
1d87950
Clean up code and improve javadocs
mateusmolina-iese Jun 4, 2024
e46390a
Improve README
mateusmolina-iese Jun 4, 2024
def7d30
Implement prefix-based delegation strategy
mateusmolina-iese Jun 5, 2024
d97ee54
Document prefix strategy in README
mateusmolina-iese Jun 5, 2024
01424df
Implement hierarchical getAllSubmodels
mateusmolina-iese Jun 5, 2024
f17c6cc
Improve test understandability
mateusmolina-iese Jun 5, 2024
8e82728
Add feature to parent/components poms
mateusmolina-iese Jun 6, 2024
d1238c0
Add example module
mateusmolina-iese Jun 6, 2024
03d2b15
Fix feature not being found during storage creation
mateusmolina-iese Jun 14, 2024
76a47eb
Refactor tests to use http for both root and delegated regs.
mateusmolina-iese Jun 14, 2024
ec5e244
Refactor hierarchy-example to use mvn docker plugin to build image
mateusmolina-iese Jun 14, 2024
9ff7164
Add example scenario IT to example
mateusmolina-iese Jun 14, 2024
cc6c83e
Add Readme to example module
mateusmolina-iese Jun 14, 2024
e727d9a
Add feature to kafka releases
mateusmolina-iese Jun 14, 2024
e796fe7
Merge branch 'main' into feat/hierarchical-registry-prototype
mateusmolina-iese Nov 28, 2024
7bbae90
feat: add SubmodelRegistry Hierarchy Feature
mateusmolina-iese Dec 2, 2024
970a799
feat: add hierarchy features to all releases
mateusmolina-iese Dec 2, 2024
6024711
feat: add submodelRegistry Hierarchy Example
mateusmolina-iese Dec 2, 2024
1f7930f
fix: aasregistry hierarchy example dockerfile failing to launch
mateusmolina-iese Dec 2, 2024
124d013
refactor: extract DelegationStrategy to common
mateusmolina-iese Dec 3, 2024
cc5ccf0
refactor: existing feature impl to use the common DelegationStrategy
mateusmolina-iese Dec 3, 2024
92bff5c
chore: update POM names
mateusmolina-iese Dec 3, 2024
4dce1f6
test: fix components not finding DelegationStrategy bean
mateusmolina-iese Dec 3, 2024
49bc8f6
test: refactor aasregistry fixture factory to be consistent with smre…
mateusmolina-iese Dec 3, 2024
bc569a5
fix: it tests not working due to common.hierarchy not being found
mateusmolina-iese Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# AssetAdministrationShell Registry - Hierarchy - Example

This example showcases the working principle of the hierarchical registries feature.

## Scenario description

```mermaid
sequenceDiagram
actor Client
participant Root as Root Registry <aas-registry-root:8080>
participant Delegated as Delegated Registry <registry.delegated-aas-registry:8080>

Client ->> Root: Request resolution for AAS-ID "http://delegated-aas-registry:8080/test/aas"
activate Root

Root ->> Root: Check local records
alt Not found locally
Root ->> Root: Determine registry based on URI prefix
Root ->> Delegated: Resolve AAS-ID "http://delegated-aas-registry:8080/test/aas" at registry.delegated-aas-registry:8080
activate Delegated
Delegated ->> Delegated: Resolve AAS-ID
Delegated ->> Root: Resolution result
deactivate Delegated
end

Root ->> Client: Return resolution result
deactivate Root
```

## Running the scenario

In order to run the example, please make sure that all aasregistries maven modules are correctly installed in your local Maven repository.

1. Generate the Docker image: `mvn clean install -Ddocker.namespace=aas-registry-test`

2. Run the docker compose: `docker compose up`

Two containers should start: (1) one for the root AAS Registry - to which the http request are going to be made; (2) one for the delegated AAS Registry - to which requests may be delegated to.

They are visibile within the bridged Docker network as (1) aas-registry-root:8080 and (2) registry.delegated-aas-registry:8080

3. Run the scenario [HierarchicalAasRegistryIT](/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/hierarchy/example/HierachicalAasRegistryIT.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
basyx:
cors:
allowed-origins: "*"
allowed-methods: "GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: "3.9"
services:

aas-registry-root:
image: aas-registry-test/aas-registry-feature-hierarchy-example:2.0.0-SNAPSHOT
container_name: aas-registry-root
ports:
- "8051:8080"
environment:
SERVER_SERVLET_CONTEXT_PATH: /
networks:
- basyx-aasregistry-feature-hierarchy-example

registry.delegated-aas-registry:
image: eclipsebasyx/aas-registry-log-mem:2.0.0-SNAPSHOT
container_name: registry.delegated-aas-registry
ports:
- "8052:8080"
environment:
SERVER_SERVLET_CONTEXT_PATH: /
volumes:
- ./aas-registry-delegated.yml:/workspace/config/application.yml
networks:
- basyx-aasregistry-feature-hierarchy-example

networks:
basyx-aasregistry-feature-hierarchy-example:
name: basyx-aasregistry-feature-hierarchy-example
driver: bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry</artifactId>
<version>${revision}</version>
</parent>

<artifactId>basyx.aasregistry-feature-hierarchy-example</artifactId>
<name>BaSyx AAS Registry Feature Hierarchy Example</name>
<description>BaSyx AAS Registry Feature Hierarchy Example</description>

<properties>
<spring-cloud.version>2020.0.4</spring-cloud.version>
<start-class> org.eclipse.digitaltwin.basyx.aasregistry.service.OpenApiGeneratorApplication</start-class>
<docker.image.name>aas-registry-feature-hierarchy-example</docker.image.name>
</properties>

<dependencies>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-service-release-log-mem</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-feature-hierarchy</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM eclipse-temurin:17 as builder
COPY maven/${project.build.finalName}.jar ./
RUN java -Djarmode=layertools -jar ${project.build.finalName}.jar extract

FROM eclipse-temurin:17
RUN mkdir /workspace
WORKDIR /workspace
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
RUN true
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENV SPRING_PROFILES_ACTIVE=logEvents,inMemoryStorage,hierarchy
ARG PORT=8080
ENV SERVER_PORT=${PORT}
ARG CONTEXT_PATH=/
ENV SERVER_SERVLET_CONTEXT_PATH=${CONTEXT_PATH}
EXPOSE ${SERVER_PORT}
HEALTHCHECK --interval=30s --timeout=3s --retries=3 --start-period=15s CMD curl --fail http://localhost:${SERVER_PORT}${SERVER_SERVLET_CONTEXT_PATH%/}/actuator/health || exit 1
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/.urandom", "org.springframework.boot.loader.launch.JarLauncher"]

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
basyx:
cors:
allowed-origins: "*"
allowed-methods: "GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD"
feature:
hierarchy:
enabled: true

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*******************************************************************************
* 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.aasregistry.feature.hierarchy.example;

import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
import org.eclipse.digitaltwin.basyx.aasregistry.feature.hierarchy.DummyDescriptorFactory;
import org.eclipse.digitaltwin.basyx.aasregistry.feature.hierarchy.HierarchicalAasRegistryTestSuite;

/**
* HierachicalAasRegistryIT
*
* @author mateusmolina
*
*/
public class HierachicalAasRegistryIT extends HierarchicalAasRegistryTestSuite {

private static final String ROOT_REGISTRY_URL = "http://localhost:8051";
private static final String DELEGATED_REGISTRY_URL = "http://localhost:8052";

private static final String REPO_BASE_URL = "http://localhost:8080";
private static final String DELEGATED_AASID = "http://delegated-aas-registry:8080/test/aas";

private static final DummyDescriptorFactory factory = new DummyDescriptorFactory(REPO_BASE_URL, DELEGATED_AASID);

private static final RegistryAndDiscoveryInterfaceApi rootRegistryApi = new RegistryAndDiscoveryInterfaceApi(ROOT_REGISTRY_URL);
private static final RegistryAndDiscoveryInterfaceApi delegatedRegistryApi = new RegistryAndDiscoveryInterfaceApi(DELEGATED_REGISTRY_URL);

@Override
protected RegistryAndDiscoveryInterfaceApi getRootRegistryApi() {
return rootRegistryApi;
}

@Override
protected RegistryAndDiscoveryInterfaceApi getDelegatedRegistryApi() {
return delegatedRegistryApi;
}

@Override
protected DummyDescriptorFactory getDescriptorFactory() {
return factory;
}

}
31 changes: 31 additions & 0 deletions basyx.aasregistry/basyx.aasregistry-feature-hierarchy/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# AssetAdministrationShell Registry - Hierarchy

The Hierarchical AasRegistry Feature enhances the availability of data across multiple registries by allowing retrieval requests to be delegated to another AasRegistry.

This feature allows the creation of a hierarchical structure of registries, where a registry can delegate retrieval requests to another registry when a given descriptor is not found in its storage.

## Configuration

### Enabling the Feature

To enable the Hierarchical AasRegistry Feature, add the following property to your Spring application's configuration:

```properties
basyx.feature.hierarchy.enabled=true
```

### Delegation Strategy

Currently, only one delegation strategy is implemented:

#### Prefix Delegation Strategy

Delegates requests based on the `aasDescriptorId` value. If the ID is an URL, a prefix (defaut `registry`) is appended to the URL and used as delegation endpoint.

The prefix can be configured by the property `basyx.feature.hierarchy.prefix`. Please refer to the example below:

```properties
basyx.feature.hierarchy.prefix=registry
```

If this property is left with an empty string, no prefix is appended to the URL contained in the `aasDecriptorId`.
38 changes: 38 additions & 0 deletions basyx.aasregistry/basyx.aasregistry-feature-hierarchy/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry</artifactId>
<version>${revision}</version>
</parent>

<artifactId>basyx.aasregistry-feature-hierarchy</artifactId>
<name>BaSyx AAS Registry feature-hierarchy</name>

<dependencies>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.hierarchy</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-service</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-service-basemodel</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-client-native</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.digitaltwin.basyx</groupId>
<artifactId>basyx.aasregistry-service-inmemory-storage</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*******************************************************************************
* 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.aasregistry.feature.hierarchy;

import java.util.List;

import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
import org.eclipse.digitaltwin.basyx.aasregistry.client.model.GetSubmodelDescriptorsResult;
import org.eclipse.digitaltwin.basyx.aasregistry.model.AssetAdministrationShellDescriptor;
import org.eclipse.digitaltwin.basyx.aasregistry.model.SubmodelDescriptor;
import org.eclipse.digitaltwin.basyx.aasregistry.service.errors.AasDescriptorNotFoundException;
import org.eclipse.digitaltwin.basyx.aasregistry.service.errors.SubmodelNotFoundException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Handles internal model mapping for {@link HierarchicalAasRegistryStorage}
*
* @author mateusmolina
*
*/
final class AasRegistryModelMapper {

private static final ObjectMapper objectMapper = new ObjectMapper();

private AasRegistryModelMapper() {
}

static org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor mapEqModel(AssetAdministrationShellDescriptor aasRegistryDescriptor) {
return objectMapper.convertValue(aasRegistryDescriptor, org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor.class);
}

static AssetAdministrationShellDescriptor mapEqModel(org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor aasRegistryDescriptor) {
return objectMapper.convertValue(aasRegistryDescriptor, AssetAdministrationShellDescriptor.class);
}

static org.eclipse.digitaltwin.basyx.aasregistry.client.model.SubmodelDescriptor mapEqModel(SubmodelDescriptor smRegistryDescriptor) {
return objectMapper.convertValue(smRegistryDescriptor, org.eclipse.digitaltwin.basyx.aasregistry.client.model.SubmodelDescriptor.class);
}

static SubmodelDescriptor mapEqModel(org.eclipse.digitaltwin.basyx.aasregistry.client.model.SubmodelDescriptor smRegistryDescriptor) {
return objectMapper.convertValue(smRegistryDescriptor, SubmodelDescriptor.class);
}

static CursorResult<List<SubmodelDescriptor>> mapEqModel(GetSubmodelDescriptorsResult descriptorResult) {
List<SubmodelDescriptor> submodelDescs = objectMapper.convertValue(descriptorResult.getResult(), new TypeReference<List<SubmodelDescriptor>>() {
});
return new CursorResult<>(descriptorResult.getPagingMetadata().getCursor(), submodelDescs);
}

static RuntimeException mapApiException(ApiException e, String aasDescriptorId) {
if (HttpStatusCode.valueOf(e.getCode()).equals(HttpStatus.NOT_FOUND))
return new AasDescriptorNotFoundException(aasDescriptorId);

return new RuntimeException(e);
}

static RuntimeException mapApiException(ApiException e, String aasDescriptorId, String smId) {
if (HttpStatusCode.valueOf(e.getCode()).equals(HttpStatus.NOT_FOUND) && checkIfSubmodelNotFound(e, aasDescriptorId, smId))
return new SubmodelNotFoundException(aasDescriptorId, smId);

return mapApiException(e, aasDescriptorId);
}

private static boolean checkIfSubmodelNotFound(ApiException e, String aasDescriptorId, String smId) {
SubmodelNotFoundException expectedException = new SubmodelNotFoundException(aasDescriptorId, smId);
return e.getMessage().contains(expectedException.getReason());
}

}
Loading
Loading