diff --git a/basyx.gateway/basyx.gateway-core/pom.xml b/basyx.gateway/basyx.gateway-core/pom.xml
index 1651f53c0..ad04353e5 100644
--- a/basyx.gateway/basyx.gateway-core/pom.xml
+++ b/basyx.gateway/basyx.gateway-core/pom.xml
@@ -13,9 +13,25 @@
BaSyx Gateway HTTP
BaSyx Gateway HTTP
+
+ org.eclipse.digitaltwin.basyx
+ basyx.core
+
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..d840c9fd6
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/DefaultGateway.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.feature.Gateway;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class DefaultGateway implements Gateway {
+
+ private Logger logger = LoggerFactory.getLogger(DefaultGateway.class);
+
+ @Override
+ public void createAAS(AssetAdministrationShell aas, String aasRepository, String aasRegistry) throws BaSyxComponentNotHealthyException{
+ throwExceptionIfIsUnhealthyBaSyxComponent(aasRepository);
+ if(aasRegistry != null){
+ throwExceptionIfIsUnhealthyBaSyxComponent(aasRegistry);
+ }
+ }
+
+ private void throwExceptionIfIsUnhealthyBaSyxComponent(String componentURL) {
+ componentURL = formatURL(componentURL);
+
+ if (isBaSyxComponent(componentURL) && !isHealthy(componentURL)) {
+ throw new BaSyxComponentNotHealthyException(componentURL + " is not healthy");
+ }
+ }
+
+ private 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;
+ }
+
+ private boolean isBaSyxComponent(String componentURL) {
+ try {
+ HttpURLConnection connection = getRequest(componentURL, "/shells");
+
+ String aasMiddleware = connection.getHeaderField("AASMiddleware");
+
+ return "BaSyx".equals(aasMiddleware);
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ return false;
+ }
+ }
+
+ private boolean isHealthy(String componentURL){
+ try {
+ HttpURLConnection connection = getRequest(componentURL, "/actuator/health");
+
+ String body = new String(connection.getInputStream().readAllBytes());
+
+ return connection.getResponseCode() == 200 && body.equals("{'status':'UP'}");
+
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ return false;
+ }
+ }
+
+ private 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/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..b3683552e
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/GatewayFactory.java
@@ -0,0 +1,8 @@
+package org.eclipse.digitaltwin.basyx.gateway.core;
+
+import org.eclipse.digitaltwin.basyx.gateway.core.feature.Gateway;
+
+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/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..2298e211c
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/main/java/org/eclipse/digitaltwin/basyx/gateway/core/feature/GatewayFeature.java
@@ -0,0 +1,8 @@
+package org.eclipse.digitaltwin.basyx.gateway.core.feature;
+
+import org.eclipse.digitaltwin.basyx.core.BaSyxFeature;
+import org.eclipse.digitaltwin.basyx.gateway.core.GatewayFactory;
+
+public interface GatewayFeature extends BaSyxFeature {
+
+}
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..e0d23a0a3
--- /dev/null
+++ b/basyx.gateway/basyx.gateway-core/src/test/java/org/eclipse/digitaltwin/basyx/gateway/TestGateway.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * 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.basyx.gateway.core.DefaultGateway;
+import org.eclipse.digitaltwin.basyx.gateway.core.exception.BaSyxComponentNotHealthyException;
+import org.eclipse.digitaltwin.basyx.gateway.core.feature.Gateway;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockserver.client.MockServerClient;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.verify.VerificationTimes;
+
+public class TestGateway {
+
+ public static ClientAndServer mockBaSyxAasRepository;
+ public static ClientAndServer mockAasRegistry;
+ private Gateway gateway;
+
+ @Before
+ public void setUp(){
+ mockBaSyxAasRepository = ClientAndServer.startClientAndServer(6006);
+ gateway = new DefaultGateway();
+ }
+
+ @After
+ public void tearDown(){
+ mockBaSyxAasRepository.stop();
+ }
+
+ @Test
+ public void testCreateAASWithHealthyBaSyxComponent(){
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/shells"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("AASMiddleware","BaSyx")
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/actuator/health"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withBody("{'status':'UP'}"));
+ gateway.createAAS(null, "http://localhost:6006", null);
+ verifyCall("/shells",1);
+ verifyCall("/actuator/health",1);
+ }
+
+ @Test(expected = BaSyxComponentNotHealthyException.class)
+ public void testCreateAASWithUnhealthyBaSyxComponent(){
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/shells"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withHeader("AASMiddleware","BaSyx")
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/actuator/health"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withBody("{'status':'DOWN'}"));
+ gateway.createAAS(null, "http://localhost:6006", null);
+ }
+
+ @Test
+ public void testCreateAASWithNonBaSyxComponent(){
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/shells"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withBody("{}"));
+ new MockServerClient("localhost", 6006)
+ .when(org.mockserver.model.HttpRequest.request()
+ .withMethod("GET")
+ .withPath("/actuator/health"))
+ .respond(org.mockserver.model.HttpResponse.response()
+ .withStatusCode(HttpStatus.SC_OK)
+ .withBody("{'status':'UP'}"));
+ gateway.createAAS(null, "http://localhost:6006", null);
+ verifyCall("/shells",1);
+ verifyCall("/actuator/health",0);
+ }
+
+ private void verifyCall(String path, int timesCalled) {
+ new MockServerClient("localhost", 6006).verify(org.mockserver.model.HttpRequest.request().withMethod("GET")
+ .withPath(path)
+ , VerificationTimes.exactly(timesCalled));
+ }
+}
diff --git a/basyx.gateway/basyx.gateway.component/pom.xml b/basyx.gateway/basyx.gateway.component/pom.xml
index 2042d284d..8c2c1c219 100644
--- a/basyx.gateway/basyx.gateway.component/pom.xml
+++ b/basyx.gateway/basyx.gateway.component/pom.xml
@@ -19,8 +19,12 @@
- org.eclipse.digitaltwin.basyx
- basyx.gateway-http
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-http
org.eclipse.digitaltwin.basyx
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
index d90886a9e..8d9e9cdc3 100644
--- 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
@@ -25,6 +25,8 @@
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;
@@ -35,6 +37,7 @@
@Component
public class ConfigurationGuard implements InitializingBean {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigurationGuard.class);
@Value("${basyx.gateway.aas-repository:#{null}}")
public String aasRepositoryURL;
@@ -52,20 +55,20 @@ public void afterPropertiesSet() throws Exception {
printWarning(missingNonRequiredProperties);
}
- System.out.println("\n:::::::::::::::: BaSyx Gateway Configuration ::::::::::::::::");
+ logger.info("\n:::::::::::::::: BaSyx Gateway Configuration ::::::::::::::::");
if (aasRepositoryURL != null) {
- System.out.println(":: Default AAS Repository URL: " + aasRepositoryURL);
+ logger.info(":: Default AAS Repository URL: " + aasRepositoryURL);
}
if (submodelRepositoryURL != null) {
- System.out.println(":: Default Submodel Repository URL: " + submodelRepositoryURL);
+ logger.info(":: Default Submodel Repository URL: " + submodelRepositoryURL);
}
if (aasRegistryURL != null) {
- System.out.println(":: Default AAS Registry URL: " + aasRegistryURL);
+ logger.info(":: Default AAS Registry URL: " + aasRegistryURL);
}
if (submodelRegistryURL != null) {
- System.out.println(":: Default Submodel Registry URL: " + submodelRegistryURL);
+ logger.info(":: Default Submodel Registry URL: " + submodelRegistryURL);
}
- System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+ logger.info(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
}
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
index 2b38e77b3..e6cbd594a 100644
--- 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
@@ -25,12 +25,10 @@
package org.eclipse.digitaltwin.basyx.gateway.component;
-import org.springframework.beans.factory.annotation.Value;
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;
-import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(
scanBasePackages = "org.eclipse.digitaltwin.basyx",
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/pom.xml b/pom.xml
index 1ca6dd76e..eb45444cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -780,6 +780,16 @@
basyx.gateway-http
${revision}
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway.component
+ ${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+ ${revision}
+
@@ -1225,7 +1235,18 @@
${revision}
tests
-
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway.component
+ ${revision}
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.gateway-core
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx
basyx.client