diff --git a/.github/workflows/basyx_test.yml b/.github/workflows/basyx_test.yml
index 1187244a2..9f49ffb5a 100644
--- a/.github/workflows/basyx_test.yml
+++ b/.github/workflows/basyx_test.yml
@@ -243,3 +243,23 @@ jobs:
- name: Stop environment
if: always()
run: docker compose --project-directory ./ci down
+ test-basyx-gateway:
+ runs-on: ubuntu-latest
+ name: Gateway Test
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+ cache: maven
+ - name: Start environment
+ run: docker compose --project-directory ./ci up -d --wait
+ - name: Build BaSyx
+ run: mvn clean install ${MVN_ARGS_BUILD_BASYX}
+ - name: Test AAS Discovery Service
+ run: mvn test -f "basyx.gateway/pom.xml"
+ - name: Stop environment
+ if: always()
+ run: docker compose --project-directory ./ci down
diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml
index d10df98fa..f00e1cca3 100644
--- a/.github/workflows/docker_test.yml
+++ b/.github/workflows/docker_test.yml
@@ -415,3 +415,29 @@ jobs:
- name: Clean up
run: exit 0
+
+ build-test-gateway:
+ runs-on: ubuntu-latest
+ name: Gateway - Build and Start Docker Image
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+ cache: maven
+ - name: Build BaSyx
+ run: |
+ mvn clean install ${MVN_ARGS_BUILD_BASYX_NO_TESTS}
+
+ - name: Build Gateway Docker Image
+ run: |
+ mvn package -DskipTests -Ddocker.namespace=test --pl "org.eclipse.digitaltwin.basyx:basyx.gateway.component"
+
+ - name: Test Gateway Docker Image
+ run: chmod +x ./.github/workflows/scripts/build_start_docker_image.sh && ./.github/workflows/scripts/build_start_docker_image.sh test/basyx-gateway ${VERSION} test_basyx_gateway
+
+ - name: Clean up
+ run: exit 0
diff --git a/basyx.gateway/basyx.gateway-core/pom.xml b/basyx.gateway/basyx.gateway-core/pom.xml
new file mode 100644
index 000000000..2ac5d2c33
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway
+ ${revision}
+
+
+ basyx.gateway-core
+ BaSyx Gateway HTTP
+ BaSyx Gateway HTTP
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-client
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasregistry-client-native
+ 2.0.0-milestone-04
+
+
+ org.eclipse.digitaltwin.aas4j
+ aas4j-model
+
+
+ org.mock-server
+ mockserver-netty
+ 5.15.0
+ test
+
+
+ org.mock-server
+ mockserver-client-java
+ 5.15.0
+ test
+
+
+
\ No newline at end of file
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/DefaultGateway.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/DefaultGateway.java
new file mode 100644
index 000000000..05fd5c843
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/DefaultGateway.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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.gateway.core;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.registryintegration.AASRegistryIntegrator;
+import org.eclipse.digitaltwin.basyx.gateway.core.utils.GatewayUtils;
+
+/**
+ * Default implementation of the Gateway interface
+ *
+ * @author fried
+ */
+public class DefaultGateway implements Gateway {
+
+ private final GatewayUtils utils = new GatewayUtils();
+
+ @Override
+ public void createAAS(AssetAdministrationShell aas, String aasRepository, String aasRegistry) throws BaSyxComponentNotHealthyException {
+ utils.validateRepository(aasRepository);
+
+ ConnectedAasRepository aasRepo = new ConnectedAasRepository(aasRepository);
+ aasRepo.createAas(aas);
+
+ if (!GatewayUtils.isRegistryDefined(aasRegistry)) {
+ return;
+ }
+
+ utils.validateRegistry(aasRegistry);
+
+ try {
+ AASRegistryIntegrator integrator = new AASRegistryIntegrator(aasRegistry);
+ integrator.registerAAS(aas, aasRepository);
+ } catch (RepositoryRegistryLinkException e) {
+ utils.handleRegistryLinkException(aasRepo, aas, aasRepository, aasRegistry, e);
+ }
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/Gateway.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/Gateway.java
new file mode 100644
index 000000000..522303862
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/Gateway.java
@@ -0,0 +1,14 @@
+package org.eclipse.digitaltwin.basyx.gateway.core;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+
+/**
+ * Gateway Interface
+ *
+ * @author fried
+ */
+public interface Gateway {
+
+ public void createAAS(AssetAdministrationShell aas, String aasRepository, String aasRegistry) throws BaSyxComponentNotHealthyException;
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/GatewayFactory.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/GatewayFactory.java
new file mode 100644
index 000000000..54c8c156d
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/GatewayFactory.java
@@ -0,0 +1,13 @@
+package org.eclipse.digitaltwin.basyx.gateway.core;
+
+import org.eclipse.digitaltwin.basyx.gateway.core.Gateway;
+
+/**
+ * Factory interface to create a Gateway instance
+ *
+ * @author fried
+ */
+public interface GatewayFactory {
+ public Gateway create();
+
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/BaSyxComponentNotHealthyException.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/BaSyxComponentNotHealthyException.java
new file mode 100644
index 000000000..493e25cc5
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/BaSyxComponentNotHealthyException.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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.gateway.core.exception;
+
+public class BaSyxComponentNotHealthyException extends RuntimeException{
+
+ public BaSyxComponentNotHealthyException(String message) {
+ super(message);
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/RegistryUnavailableException.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/RegistryUnavailableException.java
new file mode 100644
index 000000000..3648e7c49
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/exception/RegistryUnavailableException.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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.gateway.core.exception;
+
+public class RegistryUnavailableException extends RuntimeException{
+
+ public RegistryUnavailableException(String msg){
+ super(msg);
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/feature/GatewayFeature.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/feature/GatewayFeature.java
new file mode 100644
index 000000000..0ed4d459f
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/feature/GatewayFeature.java
@@ -0,0 +1,13 @@
+package org.eclipse.digitaltwin.basyx.gateway.core.feature;
+
+import org.eclipse.digitaltwin.basyx.core.BaSyxFeature;
+import org.eclipse.digitaltwin.basyx.gateway.core.GatewayFactory;
+
+/**
+ * Basic interface for Gateway Features
+ *
+ * @author fried
+ */
+public interface GatewayFeature extends BaSyxFeature {
+
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/AASRegistryIntegrator.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/AASRegistryIntegrator.java
new file mode 100644
index 000000000..b34a6c411
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/AASRegistryIntegrator.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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.gateway.core.registryintegration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.factory.AasDescriptorFactory;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.AttributeMapper;
+import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException;
+import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.util.List;
+
+/**
+ * Helper class to integrate AAS into a Registry
+ *
+ * @author fried
+ */
+public class AASRegistryIntegrator {
+ private final String aasRegistryUrl;
+ private Logger logger = LoggerFactory.getLogger(AASRegistryIntegrator.class);
+
+
+ public AASRegistryIntegrator(String aasRegistryURL){
+ this.aasRegistryUrl = aasRegistryURL;
+ }
+
+ public void registerAAS(AssetAdministrationShell aas, String aasURL){
+ integrateAasWithRegistry(aas, aasRegistryUrl, aasURL);
+ }
+
+
+ private static AttributeMapper getAttributeMapper() {
+ Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().serializationInclusion(JsonInclude.Include.NON_NULL);
+ Aas4JHTTPSerializationExtension extension = new Aas4JHTTPSerializationExtension();
+ extension.extend(builder);
+ ObjectMapper objectMapper = builder.build();
+ AttributeMapper attributeMapper = new AttributeMapper(objectMapper);
+ return attributeMapper;
+ }
+
+ private void integrateAasWithRegistry(AssetAdministrationShell shell,String aasRegistryUrl, String aasRepositoryUrl) throws RepositoryRegistryLinkException {
+ List aasRepositoryURLs = List.of(aasRepositoryUrl);
+ AttributeMapper attributeMapper = getAttributeMapper();
+
+ AssetAdministrationShellDescriptor descriptor = new AasDescriptorFactory(shell, aasRepositoryURLs, attributeMapper).create();
+
+ RegistryAndDiscoveryInterfaceApi registryApi = new RegistryAndDiscoveryInterfaceApi(aasRegistryUrl);
+
+ try {
+ registryApi.postAssetAdministrationShellDescriptor(descriptor);
+
+ } catch (ApiException e) {
+ throw new RepositoryRegistryLinkException(shell.getId(), e);
+ }
+ logger.info("Shell '{}' has been automatically linked with the Registry", shell.getId());
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/SubmodelRegistryIntegrator.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/SubmodelRegistryIntegrator.java
new file mode 100644
index 000000000..67f2e38ca
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/registryintegration/SubmodelRegistryIntegrator.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (C) 2024 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+package org.eclipse.digitaltwin.basyx.gateway.core.registryintegration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.NotImplementedException;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.factory.AasDescriptorFactory;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.AttributeMapper;
+import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException;
+import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.util.List;
+
+/**
+ * Helper class to integrate Submodel into a Registry
+ *
+ * @author fried
+ */
+public class SubmodelRegistryIntegrator {
+ private final String aasRegistryUrl;
+ private Logger logger = LoggerFactory.getLogger(SubmodelRegistryIntegrator.class);
+
+
+ public SubmodelRegistryIntegrator(String submodelRegistryURL){
+ this.aasRegistryUrl = submodelRegistryURL;
+ }
+
+ public void registerAAS(AssetAdministrationShell aas, String aasURL){
+ throw new NotImplementedException();
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/utils/GatewayUtils.java b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/utils/GatewayUtils.java
new file mode 100644
index 000000000..5c0b8636d
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/utils/GatewayUtils.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * 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.gateway.core.utils;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException;
+import org.eclipse.digitaltwin.basyx.gateway.core.DefaultGateway;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.RegistryUnavailableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class GatewayUtils {
+ private Logger logger = LoggerFactory.getLogger(GatewayUtils.class);
+
+
+ public static boolean isRegistryDefined(String aasRegistry) {
+ return aasRegistry != null;
+ }
+
+ public void validateRepository(String aasRepository) throws BaSyxComponentNotHealthyException {
+ if (aasRepository == null) {
+ throw new UnsupportedOperationException("No AAS Repository configured");
+ }
+ throwExceptionIfIsUnhealthyBaSyxRepository(aasRepository);
+ }
+
+ public void validateRegistry(String aasRegistry) throws BaSyxComponentNotHealthyException {
+ throwExceptionIfIsUnhealthyBaSyxRegistry(aasRegistry);
+ }
+
+ public void handleRegistryLinkException(ConnectedAasRepository aasRepo, AssetAdministrationShell aas, String aasRepository, String aasRegistry, RepositoryRegistryLinkException e) throws RegistryUnavailableException {
+ logger.error("Unable to link AAS {} with registry {}. Rolling back...", aas.getId(), aasRegistry);
+ aasRepo.deleteAas(aas.getId());
+ logger.error("Rollback in AAS Repository {} completed.", aasRepository);
+ throw new RegistryUnavailableException("Unable to link AAS with registry. Changes in AAS Repository rolled back.");
+ }
+
+
+ public void throwExceptionIfIsUnhealthyBaSyxRepository(String componentURL) {
+ componentURL = formatURL(componentURL);
+
+ if (isBaSyxComponent(componentURL,"/shells") && !isHealthy(componentURL)) {
+ throw new BaSyxComponentNotHealthyException(componentURL + " is not healthy");
+ }
+ }
+
+ public void throwExceptionIfIsUnhealthyBaSyxRegistry(String componentURL) {
+ componentURL = formatURL(componentURL);
+
+ if (isBaSyxComponent(componentURL,"/shell-descriptors") && !isHealthy(componentURL)) {
+ throw new BaSyxComponentNotHealthyException(componentURL + " is not healthy");
+ }
+ }
+
+ public String formatURL(String componentURL) {
+ try {
+ URL url = new URL(componentURL);
+ String protocol = url.getProtocol();
+ String host = url.getHost();
+ int port = url.getPort();
+ if (port == -1) { // no port specified, use default port
+ port = url.getDefaultPort();
+ }
+ componentURL = protocol + "://" + host + ":" + port;
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ }
+ return componentURL;
+ }
+
+ public boolean isBaSyxComponent(String componentURL, String endpointToCheck) {
+ try {
+ HttpURLConnection connection = getRequest(componentURL, "/shells");
+
+ String aasMiddleware = connection.getHeaderField("aas_middleware");
+
+ return "BaSyx".equals(aasMiddleware);
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ return false;
+ }
+ }
+
+ public boolean isHealthy(String componentURL){
+ try {
+ HttpURLConnection connection = getRequest(componentURL, "/actuator/health");
+
+ String body = new String(connection.getInputStream().readAllBytes());
+
+ return connection.getResponseCode() == 200 && body.contains("UP");
+
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ return false;
+ }
+ }
+
+ public static HttpURLConnection getRequest(String componentURL, String path) throws IOException {
+ URL url = new URL(componentURL + path);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.connect();
+ return connection;
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-core/src/test/java/org/eclipse/digitaltwin/basyx/gateway/TestGateway.java b/basyx.gateway/basyx.gateway-core/src/test/java/org/eclipse/digitaltwin/basyx/gateway/TestGateway.java
new file mode 100644
index 000000000..967296330
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/test/java/org/eclipse/digitaltwin/basyx/gateway/TestGateway.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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.gateway;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetInformation;
+import org.eclipse.digitaltwin.basyx.gateway.core.DefaultGateway;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.RegistryUnavailableException;
+import org.eclipse.digitaltwin.basyx.gateway.core.Gateway;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockserver.client.MockServerClient;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.model.HttpResponse;
+import org.mockserver.verify.VerificationTimes;
+
+public class TestGateway {
+
+ public static final String AAS_REPOSITORY_URL = "http://localhost:6006";
+ public static final String AAS_REGISTRY_URL = "http://localhost:7007";
+ public static ClientAndServer mockBaSyxAasRepository;
+ public static ClientAndServer mockAasRegistry;
+ private Gateway gateway;
+
+ @Before
+ public void setUp(){
+ mockBaSyxAasRepository = ClientAndServer.startClientAndServer(6006);
+ mockAasRegistry = ClientAndServer.startClientAndServer(7007);
+ gateway = new DefaultGateway();
+ }
+
+ @After
+ public void tearDown(){
+ mockBaSyxAasRepository.stop();
+ mockAasRegistry.stop();
+ }
+
+ @Test
+ public void testCreateAASWithHealthyBaSyxComponent(){
+ getExpectations(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("aas_middleware", "BaSyx"), "{'status':'UP'}");
+ gateway.createAAS(getDummyShell(), AAS_REPOSITORY_URL, null);
+ verifyGetCall("localhost",6006,"/shells",1);
+ verifyGetCall("localhost",6006,"/actuator/health",1);
+ }
+
+ @Test(expected = BaSyxComponentNotHealthyException.class)
+ public void testCreateAASWithUnhealthyBaSyxComponent(){
+ getExpectations(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("aas_middleware", "BaSyx"), "{'status':'DOWN'}");
+ gateway.createAAS(getDummyShell(), AAS_REPOSITORY_URL, null);
+ }
+
+ @Test
+ public void testCreateAASWithNonBaSyxComponent(){
+ getExpectations(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK), "{'status':'UP'}");
+ gateway.createAAS(getDummyShell(), AAS_REPOSITORY_URL, null);
+ verifyGetCall("localhost",6006,"/shells",1);
+ verifyGetCall("localhost",6006,"/actuator/health",0);
+ }
+ @Test(expected = RegistryUnavailableException.class)
+ public void testCreateAASWithOfflineRegistry(){
+ getExpectations(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK), "{'status':'UP'}");
+ gateway.createAAS(getDummyShell(), AAS_REPOSITORY_URL, "http://localhost:8008");
+ verifyPostCall("localhost",6006,"/shells",1);
+ verifyDeleteCall("localhost",6006,"/shells/"+getEncodedShellIdentifer(),1);
+ }
+ @Test
+ public void testCreateAASWithRegistryIntegration(){
+ getExpectations(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK), "{'status':'UP'}");
+ gateway.createAAS(getDummyShell(), AAS_REPOSITORY_URL, AAS_REGISTRY_URL);
+ verifyPostCall("localhost",6006,"/shells",1);
+ verifyPostCall("localhost",7007,"/shell-descriptors",1);
+ verifyDeleteCall("localhost",6006,"/shells/"+getEncodedShellIdentifer(),0);
+ }
+
+ private static void getExpectations(HttpResponse SC_OK, String responseBody) {
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/shells"))
+ .respond(SC_OK
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("POST")
+ .withPath("/shells"))
+ .respond(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("aas_middleware", "BaSyx")
+ .withBody("{}"));
+ new MockServerClient("localhost", 7007)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("POST")
+ .withPath("/shell-descriptors"))
+ .respond(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("aas_middleware", "BaSyx")
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("DELETE")
+ .withPath("/shells/"+getEncodedShellIdentifer()))
+ .respond(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_NO_CONTENT)
+ .withHeader("aas_middleware", "BaSyx")
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/actuator/health"))
+ .respond(HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withBody(responseBody));
+ }
+
+ private void verifyGetCall(String host, int port, String path, int timesCalled) {
+ new MockServerClient(host, port).verify(org.mockserver.model.HttpRequest.request().withMethod("GET")
+ .withPath(path)
+ , VerificationTimes.exactly(timesCalled));
+ }
+ private void verifyPostCall(String host, int port, String path, int timesCalled) {
+ new MockServerClient(host, port).verify(org.mockserver.model.HttpRequest.request().withMethod("POST")
+ .withPath(path)
+ , VerificationTimes.exactly(timesCalled));
+ }
+ private void verifyDeleteCall(String host, int port, String path, int timesCalled) {
+ new MockServerClient(host, port).verify(org.mockserver.model.HttpRequest.request().withMethod("DELETE")
+ .withPath(path)
+ , VerificationTimes.exactly(timesCalled));
+ }
+
+ private static AssetAdministrationShell getDummyShell(){
+ return new DefaultAssetAdministrationShell.Builder().id("TestId").idShort("test").assetInformation(new DefaultAssetInformation.Builder().build()).build();
+ }
+
+ private static String getEncodedShellIdentifer(){
+ return new Base64UrlEncodedIdentifier(getDummyShell().getId()).getEncodedIdentifier();
+ }
+}
diff --git a/basyx.gateway/basyx.gateway-http/pom.xml b/basyx.gateway/basyx.gateway-http/pom.xml
new file mode 100644
index 000000000..0fb1df80b
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-http/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway
+ ${revision}
+
+
+ basyx.gateway-http
+ BaSyx Gateway HTTP
+ BaSyx Gateway HTTP
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+
+
+ org.eclipse.digitaltwin.aas4j
+ aas4j-model
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ org.eclipse.digitaltwin.aas4j
+ aas4j-dataformat-json
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+ commons-io
+ commons-io
+ test
+
+
+
\ No newline at end of file
diff --git a/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApi.java b/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApi.java
new file mode 100644
index 000000000..32eaa767c
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApi.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.gateway.http;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.validation.Valid;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.aas4j.v3.model.Result;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@Validated
+public interface GatewayHTTPApi {
+
+ @Operation(summary = "Creates a new Asset Administration Shell", description = "", tags = { "Asset Administration Shell Repository API" })
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "201", description = "Asset Administration Shell created successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AssetAdministrationShell.class))),
+
+ @ApiResponse(responseCode = "400", description = "Bad Request, e.g. the request parameters of the format of the request body is wrong.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "401", description = "Unauthorized, e.g. the server refused the authorization attempt.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "409", description = "Conflict, a resource which shall be created exists already. Might be thrown if a Submodel or SubmodelElement with the same ShortId is contained in a POST request.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "200", description = "Default error handling for unmentioned status codes", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) })
+ @RequestMapping(value = "/shells", produces = { "application/json" }, consumes = { "application/json" }, method = RequestMethod.POST)
+ ResponseEntity> postAssetAdministrationShell(
+ @Parameter(in = ParameterIn.DEFAULT, description = "Asset Administration Shell object", required = true, schema = @Schema()) @Valid @RequestBody AssetAdministrationShell body,
+ @Parameter(in = ParameterIn.QUERY, description = "The Asset Administration Shell Repository URL", schema = @Schema()) @RequestParam(value = "aasRepositoryURL", required = false) String aasRepositoryURL,
+ @Parameter(in = ParameterIn.QUERY, description = "The Asset Administration Shell Registry URL", schema = @Schema()) @RequestParam(value = "aasRegistryURL", required = false) String aasRegistryURL
+
+ );
+
+}
diff --git a/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApiController.java b/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApiController.java
new file mode 100644
index 000000000..327d6033f
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-http/src/main/java/org/eclipse/digitaltwin/basyx/gateway/http/GatewayHTTPApiController.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.gateway.http;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.Gateway;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@ConditionalOnProperty(name = "basyx.gateway.aas-repository")
+public class GatewayHTTPApiController implements GatewayHTTPApi{
+ @Value("${basyx.gateway.aas-repository:#{null}}")
+ public String aasRepositoryURL;
+ @Value("${basyx.gateway.submodel-repository:#{null}}")
+ public String submodelRepositoryURL;
+ @Value("${basyx.gateway.aas-registry:#{null}}")
+ public String aasRegistryURL;
+ @Value("${basyx.gateway.submodel-registry:#{null}}")
+ public String submodelRegistryURL;
+
+ private final Gateway gateway;
+
+ public GatewayHTTPApiController(Gateway gateway){
+ this.gateway = gateway;
+ }
+
+ @Override
+ public ResponseEntity> postAssetAdministrationShell(AssetAdministrationShell body, String aasRepositoryURL, String aasRegistryURL) {
+ if(!isAnyURLSet(aasRepositoryURL, this.aasRepositoryURL)){
+ return ResponseEntity.badRequest().body("No AAS Repository URL set");
+ }
+ try {
+ gateway.createAAS(body, aasRepositoryURL == null ? this.aasRepositoryURL : aasRepositoryURL, aasRegistryURL);
+ }catch(BaSyxComponentNotHealthyException e){
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
+
+ }
+ return new ResponseEntity(body, HttpStatus.CREATED);
+ }
+
+ private static boolean isAnyURLSet(String url1, String url2) {
+ return url1 != null || url2 != null;
+ }
+
+
+}
diff --git a/basyx.gateway/basyx.gateway.component/Dockerfile b/basyx.gateway/basyx.gateway.component/Dockerfile
new file mode 100644
index 000000000..850188a4c
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/Dockerfile
@@ -0,0 +1,11 @@
+FROM amazoncorretto:17
+USER nobody
+WORKDIR /application
+ARG JAR_FILE=target/*-exec.jar
+COPY ${JAR_FILE} basyxExecutable.jar
+COPY src/main/resources/application.properties application.properties
+ARG PORT=8081
+ENV SERVER_PORT=${PORT}
+EXPOSE ${SERVER_PORT}
+HEALTHCHECK --interval=30s --timeout=3s --retries=3 --start-period=30s CMD curl --fail http://localhost:${SERVER_PORT}/actuator/health || exit 1
+ENTRYPOINT ["java","-jar","basyxExecutable.jar"]
diff --git a/basyx.gateway/basyx.gateway.component/pom.xml b/basyx.gateway/basyx.gateway.component/pom.xml
new file mode 100644
index 000000000..7eecfef9b
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/pom.xml
@@ -0,0 +1,149 @@
+
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway
+ ${revision}
+
+
+ basyx.gateway.component
+ BaSyx Gateway Component
+ BaSyx Gateway Component
+
+
+ basyx-gateway
+
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-client
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-http
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+ commons-io
+ commons-io
+
+
+ com.google.code.gson
+ gson
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ exec
+
+
+
+
+
+
+
+ docker
+
+
+ docker.namespace
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+
+
+
+ ${docker.host.port}:${docker.container.port}
+
+
+
+ ${docker.container.waitForEndpoint}
+
+
+
+
+
+
+
+
+ build-docker
+
+
+ push-docker
+
+
+ docker-compose-up
+ pre-integration-test
+
+ start
+
+
+
+ docker-compose-down
+ post-integration-test
+
+ stop
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/ConfigurationGuard.java b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/ConfigurationGuard.java
new file mode 100644
index 000000000..c458e9564
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/ConfigurationGuard.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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.gateway.component;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Class that prints error and warning messages to inform the user about possible misconfiguration
+ *
+ * @author fried
+ */
+@Component
+public class ConfigurationGuard implements InitializingBean {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigurationGuard.class);
+
+ @Value("${basyx.gateway.aas-repository:#{null}}")
+ public String aasRepositoryURL;
+ @Value("${basyx.gateway.submodel-repository:#{null}}")
+ public String submodelRepositoryURL;
+ @Value("${basyx.gateway.aas-registry:#{null}}")
+ public String aasRegistryURL;
+ @Value("${basyx.gateway.submodel-registry:#{null}}")
+ public String submodelRegistryURL;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ List missingNonRequiredProperties = getMissingNonRequiredProperties();
+ if (!missingNonRequiredProperties.isEmpty()) {
+ printWarning(missingNonRequiredProperties);
+ }
+
+ logger.info(":::::::::::::::: BaSyx Gateway Configuration ::::::::::::::::");
+ if (aasRepositoryURL != null) {
+ logger.info(":: Default AAS Repository URL: " + aasRepositoryURL);
+ }
+ if (submodelRepositoryURL != null) {
+ logger.info(":: Default Submodel Repository URL: " + submodelRepositoryURL);
+ }
+ if (aasRegistryURL != null) {
+ logger.info(":: Default AAS Registry URL: " + aasRegistryURL);
+ }
+ if (submodelRegistryURL != null) {
+ logger.info(":: Default Submodel Registry URL: " + submodelRegistryURL);
+ }
+ logger.info(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+ }
+
+
+ private static void printWarning(List missingNonRequiredProperties) {
+ System.err.println(
+ "\n:::::: BaSyx Gateway Startup Warning ::::::" +
+ "\n\nThe following, recommended, properties are missing: \n"
+ + missingNonRequiredProperties.stream()
+ .map(prop -> "- " + prop)
+ .collect(Collectors.joining("\n"))+"\n"
+ );
+ }
+
+ private List getMissingNonRequiredProperties() {
+ List missingNonRequiredProperties = new ArrayList<>();
+ if(this.aasRegistryURL == null) {
+ missingNonRequiredProperties.add("basyx.gateway.aas-registry");
+ }
+ if(this.submodelRegistryURL == null) {
+ missingNonRequiredProperties.add("basyx.gateway.submodel-registry");
+ }
+ if(this.aasRepositoryURL == null) {
+ missingNonRequiredProperties.add("basyx.gateway.aas-repository");
+ }
+ if(this.submodelRegistryURL == null) {
+ missingNonRequiredProperties.add("basyx.gateway.submodel-repository");
+ }
+ return missingNonRequiredProperties;
+ }
+}
diff --git a/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponent.java b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponent.java
new file mode 100644
index 000000000..e6cbd594a
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponent.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.gateway.component;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
+import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
+
+@SpringBootApplication(
+ scanBasePackages = "org.eclipse.digitaltwin.basyx",
+ exclude = { MongoAutoConfiguration.class, MongoDataAutoConfiguration.class })
+public class GatewayComponent {
+ public static void main(String[] args) {
+ SpringApplication.run(GatewayComponent.class, args);
+ }
+}
diff --git a/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponentConfiguration.java b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponentConfiguration.java
new file mode 100644
index 000000000..d2070b46e
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayComponentConfiguration.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.gateway.component;
+
+import org.eclipse.digitaltwin.basyx.gateway.core.DefaultGateway;
+import org.eclipse.digitaltwin.basyx.gateway.core.Gateway;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GatewayComponentConfiguration {
+
+ @Bean
+ public Gateway getGateway() {
+ return new DefaultGateway();
+ }
+}
diff --git a/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayFeaturePrinter.java b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayFeaturePrinter.java
new file mode 100644
index 000000000..982868834
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/java/org/eclipse/digitaltwin/basyx/gateway/component/GatewayFeaturePrinter.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (C) 2023 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+
+package org.eclipse.digitaltwin.basyx.gateway.component;
+import java.util.List;
+
+import org.eclipse.digitaltwin.basyx.gateway.core.feature.GatewayFeature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Prints all SubmodelRepository features that are on the classpath
+ *
+ * @author schnicke
+ *
+ */
+@Service
+public class GatewayFeaturePrinter {
+
+ private static final Logger logger = LoggerFactory.getLogger(GatewayFeaturePrinter.class);
+
+ @Autowired
+ public GatewayFeaturePrinter(List features) {
+ logger.info("-------------------- BaSyx Gateway Features: --------------------");
+ if(features.isEmpty()){
+ logger.info("No BaSyx Gateway Features found");
+ }
+ for (GatewayFeature feature : features) {
+ logger.info("BaSyxFeature " + feature.getName() + " is enabled: " + feature.isEnabled());
+ }
+
+ logger.info("----------------------------------------------------------------- ");
+ }
+}
diff --git a/basyx.gateway/basyx.gateway.component/src/main/resources/application.properties b/basyx.gateway/basyx.gateway.component/src/main/resources/application.properties
new file mode 100644
index 000000000..b3934437f
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+server.port=5999
+
+
+#######################################################
+# Default AAS/Submodel Repository URL (optional, recommended) #
+#######################################################
+basyx.gateway.aas-repository=http://localhost:8081
+#basyx.gateway.submodel-repository=http://localhost:8082
+
+#############################################################
+# Default AAS/Submodel Registry URL (optional, recommended) #
+#############################################################
+#basyx.gateway.aas-registry=http://localhost:8083
+#basyx.gateway.submodel-registry=http://localhost:8084
diff --git a/basyx.gateway/basyx.gateway.component/src/main/resources/banner.txt b/basyx.gateway/basyx.gateway.component/src/main/resources/banner.txt
new file mode 100644
index 000000000..3cfa60cfb
--- /dev/null
+++ b/basyx.gateway/basyx.gateway.component/src/main/resources/banner.txt
@@ -0,0 +1,7 @@
+ ____ ____ ____ _
+| __ ) __ _/ ___| _ ___ __ / ___| __ _| |_ _____ ____ _ _ _
+| _ \ / _` \___ \| | | \ \/ / | | _ / _` | __/ _ \ \ /\ / / _` | | | |
+| |_) | (_| |___) | |_| |> < | |_| | (_| | || __/\ V V / (_| | |_| |
+|____/ \__,_|____/ \__, /_/\_\ \____|\__,_|\__\___| \_/\_/ \__,_|\__, |
+===================|___/==========================================|___/
+2.0.0-SNAPSHOT
diff --git a/basyx.gateway/pom.xml b/basyx.gateway/pom.xml
new file mode 100644
index 000000000..fd2280694
--- /dev/null
+++ b/basyx.gateway/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.parent
+ ${revision}
+
+
+ basyx.gateway
+ BaSyx Gateway
+ Component handling Registry Integration for distributed systems
+ pom
+
+
+ basyx.gateway-http
+ basyx.gateway-core
+ basyx.gateway.component
+
+
\ No newline at end of file
diff --git a/examples/BaSyxSecured/docker-compose.yaml b/examples/BaSyxSecured/docker-compose.yaml
index 458a71e35..a1297f083 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:v2-240913
container_name: aas-ui
extra_hosts:
- "keycloak:127.0.0.1"
diff --git a/pom.xml b/pom.xml
index 953e70db9..eb45444cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@
basyx.conceptdescriptionrepositorybasyx.aasxfileserverbasyx.aasdiscoveryservice
+ basyx.gatewayBaSyx ParentParent POM for Eclipse BaSyx
@@ -773,6 +774,22 @@
basyx.submodelregistry-client-native${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-http
+ ${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway.component
+ ${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+ ${revision}
+
@@ -1211,6 +1228,25 @@
${revision}tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-http
+ ${revision}
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway.component
+ ${revision}
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+ ${revision}
+ tests
+ org.eclipse.digitaltwin.basyxbasyx.client