You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2023/12/30 18:09:28 UTC

(plc4x) branch feature/new-ui-tool updated: feat: Implemented a way to send server-events to the frontend using websockets (Implemented on the server-side and not yet in the frontend application, receiving messages using ws.html only)

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/new-ui-tool
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/new-ui-tool by this push:
     new c0d4fc84fe feat: Implemented a way to send server-events to the frontend using websockets (Implemented on the server-side and not yet in the frontend application, receiving messages using ws.html only)
c0d4fc84fe is described below

commit c0d4fc84fe1a08333365c6c716a5b51c70ff21cc
Author: Christofer Dutz <cd...@apache.org>
AuthorDate: Sat Dec 30 19:09:19 2023 +0100

    feat: Implemented a way to send server-events to the frontend using websockets (Implemented on the server-side and not yet in the frontend application, receiving messages using ws.html only)
---
 plc4j/tools/ui/backend/pom.xml                     |  81 ++++++++++++-----
 .../java/tools/ui/Plc4xUiBackendApplication.java   |   9 +-
 .../Plc4xConfiguration.java}                       |  16 ++--
 .../tools/ui/configuration/WebSocketConfig.java    |  43 +++++++++
 .../tools/ui/controller/ConnectionController.java  |  62 +++++++++++++
 .../java/tools/ui/controller/DriverController.java |  69 ++++----------
 .../ApplicationEvent.java}                         |  22 +++--
 .../DeviceEvent.java}                              |  12 +--
 .../EventType.java}                                |  14 +--
 .../java/tools/ui/handler/WebSocketHandler.java    | 101 +++++++++++++++++++++
 .../ConnectionService.java => model/Device.java}   |  25 +++--
 .../ConnectionRepository.java}                     |  14 +--
 .../java/tools/ui/service/ConnectionService.java   |  37 +++++++-
 .../DriverService.java}                            |  72 +++++++++------
 .../src/main/resources/application.properties      |  10 ++
 plc4j/tools/ui/backend/ws.html                     |  36 ++++++++
 plc4j/tools/ui/frontend/frontend/package.json      |   9 ++
 .../src/generated/plc4j-tools-ui-frontend.ts       |  42 ++++++---
 .../ui/frontend/frontend/src/pages/Inspect.tsx     |   8 +-
 plc4j/tools/ui/frontend/pom.xml                    |   1 +
 plc4j/tools/ui/pom.xml                             |  29 ++++++
 21 files changed, 540 insertions(+), 172 deletions(-)

diff --git a/plc4j/tools/ui/backend/pom.xml b/plc4j/tools/ui/backend/pom.xml
index 0d5f8a1633..78cce151c8 100644
--- a/plc4j/tools/ui/backend/pom.xml
+++ b/plc4j/tools/ui/backend/pom.xml
@@ -37,6 +37,10 @@
           <usedDependencies combine.children="append">
             <usedDependency>org.apache.plc4x:plc4j-driver-all</usedDependency>
             <usedDependency>org.springframework.boot:spring-boot-starter-web</usedDependency>
+            <usedDependency>org.springframework.boot:spring-boot-starter-websocket</usedDependency>
+            <usedDependency>org.springframework.boot:spring-boot-starter-data-jpa</usedDependency>
+            <usedDependency>org.apache.derby:derby</usedDependency>
+            <usedDependency>org.apache.derby:derbytools</usedDependency>
             <usedDependency>ch.qos.logback:logback-core</usedDependency>
           </usedDependencies>
         </configuration>
@@ -47,23 +51,57 @@
   <dependencies>
     <dependency>
       <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-web</artifactId>
-      <version>3.1.5</version>
+      <artifactId>spring-boot</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-autoconfigure</artifactId>
-      <version>3.1.5</version>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot</artifactId>
-      <version>3.1.5</version>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-websocket</artifactId>
+    </dependency>
+    <!--dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+    </dependency-->
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-web</artifactId>
-      <version>6.0.13</version>
+    </dependency>
+    <!--dependency>
+      <groupId>org.springframework.data</groupId>
+      <artifactId>spring-data-commons</artifactId>
+    </dependency-->
+    <dependency>
+      <groupId>org.springframework.data</groupId>
+      <artifactId>spring-data-jpa</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-websocket</artifactId>
+    </dependency>
+
+    <!-- Add support for the embedded derby database, which is used for saving the configuration -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-jpa</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derbytools</artifactId>
     </dependency>
 
     <dependency>
@@ -88,10 +126,22 @@
       <type>pom</type>
     </dependency>
 
+    <dependency>
+      <groupId>jakarta.persistence</groupId>
+      <artifactId>jakarta.persistence-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <!--dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency-->
 
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -112,42 +162,23 @@
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
-      <version>3.1.5</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-test</artifactId>
-      <version>3.1.5</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
-      <version>6.0.13</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.springframework.security</groupId>
       <artifactId>spring-security-test</artifactId>
-      <version>6.1.5</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
 
-  <dependencyManagement>
-    <dependencies>
-      <dependency>
-        <groupId>org.aspectj</groupId>
-        <artifactId>aspectjweaver</artifactId>
-        <version>1.9.20.1</version>
-      </dependency>
-      <dependency>
-        <groupId>jakarta.annotation</groupId>
-        <artifactId>jakarta.annotation-api</artifactId>
-        <version>2.1.1</version>
-      </dependency>
-    </dependencies>
-  </dependencyManagement>
-
 </project>
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/Plc4xUiBackendApplication.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/Plc4xUiBackendApplication.java
index 79dbec6a7b..c6290bef30 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/Plc4xUiBackendApplication.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/Plc4xUiBackendApplication.java
@@ -21,13 +21,14 @@ package org.apache.plc4x.java.tools.ui;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
 
 @SpringBootApplication
+@EnableJpaRepositories("org.apache.plc4x.java.tools.ui")
 public class Plc4xUiBackendApplication {
 
-	public static void main(String[] args) {
-		SpringApplication.run(Plc4xUiBackendApplication.class, args);
-	}
+    public static void main(String[] args) {
+        SpringApplication.run(Plc4xUiBackendApplication.class, args);
+    }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/Plc4xConfiguration.java
similarity index 68%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/Plc4xConfiguration.java
index 1d2299a4f9..1b79413fd6 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/Plc4xConfiguration.java
@@ -17,16 +17,18 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.configuration;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+import org.apache.plc4x.java.api.PlcDriverManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
 
-public class ConnectionService {
+@Configuration
+public class Plc4xConfiguration {
 
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
-
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
+    @Bean
+    public PlcDriverManager driverManager() {
+        return PlcDriverManager.getDefault();
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/WebSocketConfig.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/WebSocketConfig.java
new file mode 100644
index 0000000000..18d3d0f98d
--- /dev/null
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/configuration/WebSocketConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.plc4x.java.tools.ui.configuration;
+
+import org.apache.plc4x.java.tools.ui.handler.WebSocketHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig implements WebSocketConfigurer {
+
+    private final WebSocketHandler webSocketHandler;
+
+    public WebSocketConfig(WebSocketHandler webSocketHandler) {
+        this.webSocketHandler = webSocketHandler;
+    }
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+        registry.addHandler(webSocketHandler, "/ws").setAllowedOrigins("*");
+    }
+
+}
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/ConnectionController.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/ConnectionController.java
new file mode 100644
index 0000000000..9f5ef46bb9
--- /dev/null
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/ConnectionController.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.plc4x.java.tools.ui.controller;
+
+import org.apache.plc4x.java.tools.ui.model.Device;
+import org.apache.plc4x.java.tools.ui.service.ConnectionService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/connections")
+public class ConnectionController {
+
+    private final ConnectionService connectionService;
+
+    public ConnectionController(ConnectionService connectionService) {
+        this.connectionService = connectionService;
+    }
+
+    @GetMapping("/{id}")
+    public Device getConnectionById(@PathVariable Integer id) {
+        return connectionService.readConnection(id).orElseThrow(() -> new RuntimeException("Error finding connection with id: " + id));
+    }
+
+    @GetMapping
+    public List<Device> getAllConnections() {
+        return connectionService.getAllConnections();
+    }
+
+    @PostMapping
+    public Device saveConnection(@RequestBody Device device) {
+        if (device.getId() == null) {
+            return connectionService.createConnection(device);
+        } else {
+            return connectionService.updateConnection(device);
+        }
+    }
+
+    @DeleteMapping
+    public void deleteConnection(@RequestBody Device device) {
+        connectionService.deleteConnection(device);
+    }
+
+}
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java
index 8919f284dc..3b7c7e7c73 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java
@@ -19,70 +19,33 @@
 
 package org.apache.plc4x.java.tools.ui.controller;
 
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.plc4x.java.api.PlcDriver;
-import org.apache.plc4x.java.api.PlcDriverManager;
-import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;
-import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
-import org.apache.plc4x.java.spi.configuration.annotations.ComplexConfigurationParameter;
-import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
-import org.apache.plc4x.java.spi.configuration.annotations.Required;
-import org.apache.plc4x.java.tools.ui.model.ConfigurationOption;
 import org.apache.plc4x.java.tools.ui.model.Driver;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.apache.plc4x.java.tools.ui.service.DriverService;
+import org.springframework.web.bind.annotation.*;
 
-import java.lang.reflect.Field;
-import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.List;
 
 // Allow from the default port 8080 as well as the one node usually uses for it's dev-mode 5173
-@CrossOrigin(origins = {"http://localhost:8080", "http://localhost:5173"})
+//@CrossOrigin(origins = {"http://localhost:8080", "http://localhost:5173"})
+@CrossOrigin(origins = {"*"})
 @RestController
 @RequestMapping("/api")
 public class DriverController {
 
-    @GetMapping("/drivers")
-    public ResponseEntity<List<Driver>> getDriverList() {
-        List<Driver> drivers = new ArrayList<>();
-
-        // Build a list of driver objects.
-        PlcDriverManager driverManager = PlcDriverManager.getDefault();
-        for (String protocolCode : driverManager.listDrivers()) {
-            try {
-                PlcDriver driver = driverManager.getDriver(protocolCode);
-                PlcDriverMetadata metadata = driver.getMetadata();
-
-                // Get a description of all supported configuration options of the given driver.
-                Class<?> configurationType = driver.getConfigurationType();
-                Map<String, ConfigurationOption> configurationOptions = Arrays.stream(FieldUtils.getAllFields(configurationType))
-                    // - Filter out only the ones annotated with the ConfigurationParameter annotation.
-                    .filter(field -> (field.getAnnotation(ConfigurationParameter.class) != null) || (field.getAnnotation(ComplexConfigurationParameter.class) != null))
-                    .map(field -> new ConfigurationOption(field.getName(), field.getType().getTypeName(), field.isAnnotationPresent(Required.class), ConfigurationFactory.getDefaultValueFromAnnotation(field)))
-                    // - Create a map with the field-name as key and the field itself as value.
-                    .collect(Collectors.toMap(
-                        ConfigurationOption::getName,
-                        Function.identity()
-                    ));
-
-                // TODO: Get a list of all directly supported transports and for each a list of the configuration options.
+    private final DriverService driverService;
 
-                drivers.add(new Driver(protocolCode, driver.getProtocolName(), metadata.canDiscover(), configurationOptions, null));
-            } catch (Exception e) {
-                e.printStackTrace();
-                // Ignore ...
-            }
-        }
+    public DriverController(DriverService driverService) {
+        this.driverService = driverService;
+    }
 
-        // Sort the list by the code of the driver elements.
-        drivers.sort(Comparator.comparing(Driver::getCode));
+    @GetMapping("/drivers")
+    public List<Driver> getDriverList() {
+        return driverService.getDriverList();
+    }
 
-        return ResponseEntity.ok(drivers);
+    @GetMapping("/discover/{protocolCode}")
+    public void discover(@PathVariable("protocolCode") String protocolCode) {
+        driverService.discover(protocolCode);
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/ApplicationEvent.java
similarity index 58%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/ApplicationEvent.java
index 1d2299a4f9..6eab967421 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/ApplicationEvent.java
@@ -17,16 +17,26 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.event;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+public abstract class ApplicationEvent<T> extends org.springframework.context.ApplicationEvent {
 
-public class ConnectionService {
+    private final EventType eventType;
 
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
+    public ApplicationEvent(T source, EventType eventType) {
+        super(source);
+        this.eventType = eventType;
+    }
+
+    public EventType getEventType() {
+        return eventType;
+    }
 
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
+    @Override
+    public T getSource() {
+        // We're only accepting objects of type T, so in this case it's safe to cast.
+        //noinspection unchecked
+        return (T) super.getSource();
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/DeviceEvent.java
similarity index 70%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/DeviceEvent.java
index 1d2299a4f9..d9693b7c39 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/DeviceEvent.java
@@ -17,16 +17,14 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.event;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+import org.apache.plc4x.java.tools.ui.model.Device;
 
-public class ConnectionService {
+public class DeviceEvent extends ApplicationEvent<Device> {
 
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
-
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
+    public DeviceEvent(Device source, EventType eventType) {
+        super(source, eventType);
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/EventType.java
similarity index 69%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/EventType.java
index 1d2299a4f9..a7613333b1 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/event/EventType.java
@@ -17,16 +17,12 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.event;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+public enum EventType {
 
-public class ConnectionService {
-
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
-
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
-    }
+    CREATED,
+    UPDATED,
+    DELETED
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/handler/WebSocketHandler.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/handler/WebSocketHandler.java
new file mode 100644
index 0000000000..8dbd33dea2
--- /dev/null
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/handler/WebSocketHandler.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.plc4x.java.tools.ui.handler;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.plc4x.java.tools.ui.event.ApplicationEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class WebSocketHandler implements org.springframework.web.socket.WebSocketHandler {
+
+    private final Map<String, WebSocketSession> openSessions = new ConcurrentHashMap<>();
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * ApplicationEvent listener forwarding all ApplicationEvents from this application to all connected clients.
+     * @param applicationEvent event that we want to be forwarded.
+     */
+    @EventListener
+    public void onApplicationEvent(ApplicationEvent<?> applicationEvent) {
+        openSessions.forEach((s, webSocketSession) -> {
+            try {
+                webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(applicationEvent)));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    /**
+     * Register a new web-socket session.
+     * @param session the new web-socket session.
+     */
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) {
+        openSessions.put(session.getId(), session);
+    }
+
+    /**
+     * Remove a web-socket session from the list.
+     * @param session the web-socket session we want to remove
+     * @param closeStatus the status of the closed session
+     */
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {
+        openSessions.remove(session.getId());
+    }
+
+    /**
+     * Handle an incoming message on the web-socket session
+     * (this should actually not happen, as we only use the web-sockets for server-to-client communication)
+     * @param session the web-socket session the message is coming in on
+     * @param message the message
+     */
+    @Override
+    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
+        System.out.println("handleMessage");
+    }
+
+    /**
+     * Callback for handling transport errors.
+     * @param session the web-socket session causing the error
+     * @param exception the error that happened
+     */
+    @Override
+    public void handleTransportError(WebSocketSession session, Throwable exception) {
+        System.out.println("handleTransportError");
+    }
+
+    @Override
+    public boolean supportsPartialMessages() {
+        return false;
+    }
+
+}
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/model/Device.java
similarity index 64%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/model/Device.java
index 1d2299a4f9..4e7e702fa8 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/model/Device.java
@@ -17,16 +17,29 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.model;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
 
-public class ConnectionService {
+@Entity
+public class Device {
 
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
 
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
+    public Device() {
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/repository/ConnectionRepository.java
similarity index 69%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/repository/ConnectionRepository.java
index 1d2299a4f9..52b0de3f56 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/repository/ConnectionRepository.java
@@ -17,16 +17,10 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.service;
+package org.apache.plc4x.java.tools.ui.repository;
 
-import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
-
-public class ConnectionService {
-
-    private final CachedPlcConnectionManager cachedPlcConnectionManager;
-
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
-    }
+import org.apache.plc4x.java.tools.ui.model.Device;
+import org.springframework.data.jpa.repository.JpaRepository;
 
+public interface ConnectionRepository extends JpaRepository<Device, Integer> {
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
index 1d2299a4f9..2cffd94501 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/ConnectionService.java
@@ -19,14 +19,47 @@
 
 package org.apache.plc4x.java.tools.ui.service;
 
+import org.apache.plc4x.java.api.PlcDriverManager;
+import org.apache.plc4x.java.tools.ui.model.Device;
+import org.apache.plc4x.java.tools.ui.repository.ConnectionRepository;
 import org.apache.plc4x.java.utils.cache.CachedPlcConnectionManager;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
 
+import java.util.List;
+import java.util.Optional;
+
+@Component
 public class ConnectionService {
 
+    private final ConnectionRepository connectionRepository;
+    private final ApplicationEventPublisher publisher;
     private final CachedPlcConnectionManager cachedPlcConnectionManager;
 
-    public ConnectionService() {
-        cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder().build();
+    public ConnectionService(ConnectionRepository connectionRepository, ApplicationEventPublisher publisher, PlcDriverManager driverManager) {
+        this.connectionRepository = connectionRepository;
+        this.publisher = publisher;
+        this.cachedPlcConnectionManager = CachedPlcConnectionManager.getBuilder(driverManager.getConnectionManager()).build();
+    }
+
+    public List<Device> getAllConnections() {
+        return connectionRepository.findAll();
+    }
+
+    public Device createConnection(Device device) {
+        return connectionRepository.save(device);
+    }
+
+    public Optional<Device> readConnection(Integer id) {
+        return connectionRepository.findById(id);
+    }
+
+    public Device updateConnection(Device device) {
+        return connectionRepository.save(device);
+    }
+
+    public void deleteConnection(Device device) {
+        connectionRepository.delete(device);
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/DriverService.java
similarity index 58%
copy from plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java
copy to plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/DriverService.java
index 8919f284dc..170d518cc9 100644
--- a/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/controller/DriverController.java
+++ b/plc4j/tools/ui/backend/src/main/java/org/apache/plc4x/java/tools/ui/service/DriverService.java
@@ -17,42 +17,47 @@
  * under the License.
  */
 
-package org.apache.plc4x.java.tools.ui.controller;
+package org.apache.plc4x.java.tools.ui.service;
 
 import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.plc4x.java.api.PlcDriver;
 import org.apache.plc4x.java.api.PlcDriverManager;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
 import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;
 import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
 import org.apache.plc4x.java.spi.configuration.annotations.ComplexConfigurationParameter;
 import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
 import org.apache.plc4x.java.spi.configuration.annotations.Required;
+import org.apache.plc4x.java.tools.ui.event.DeviceEvent;
+import org.apache.plc4x.java.tools.ui.event.EventType;
 import org.apache.plc4x.java.tools.ui.model.ConfigurationOption;
+import org.apache.plc4x.java.tools.ui.model.Device;
 import org.apache.plc4x.java.tools.ui.model.Driver;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
 
-import java.lang.reflect.Field;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-// Allow from the default port 8080 as well as the one node usually uses for it's dev-mode 5173
-@CrossOrigin(origins = {"http://localhost:8080", "http://localhost:5173"})
-@RestController
-@RequestMapping("/api")
-public class DriverController {
+@Component
+public class DriverService {
 
-    @GetMapping("/drivers")
-    public ResponseEntity<List<Driver>> getDriverList() {
-        List<Driver> drivers = new ArrayList<>();
+    private final PlcDriverManager driverManager;
+
+    private final ApplicationEventPublisher applicationEventPublisher;
 
-        // Build a list of driver objects.
-        PlcDriverManager driverManager = PlcDriverManager.getDefault();
+    public DriverService(PlcDriverManager driverManager, ApplicationEventPublisher applicationEventPublisher) {
+        this.driverManager = driverManager;
+        this.applicationEventPublisher = applicationEventPublisher;
+    }
+
+    public List<Driver> getDriverList() {
+        List<Driver> drivers = new ArrayList<>();
         for (String protocolCode : driverManager.listDrivers()) {
             try {
                 PlcDriver driver = driverManager.getDriver(protocolCode);
@@ -61,10 +66,8 @@ public class DriverController {
                 // Get a description of all supported configuration options of the given driver.
                 Class<?> configurationType = driver.getConfigurationType();
                 Map<String, ConfigurationOption> configurationOptions = Arrays.stream(FieldUtils.getAllFields(configurationType))
-                    // - Filter out only the ones annotated with the ConfigurationParameter annotation.
                     .filter(field -> (field.getAnnotation(ConfigurationParameter.class) != null) || (field.getAnnotation(ComplexConfigurationParameter.class) != null))
                     .map(field -> new ConfigurationOption(field.getName(), field.getType().getTypeName(), field.isAnnotationPresent(Required.class), ConfigurationFactory.getDefaultValueFromAnnotation(field)))
-                    // - Create a map with the field-name as key and the field itself as value.
                     .collect(Collectors.toMap(
                         ConfigurationOption::getName,
                         Function.identity()
@@ -74,15 +77,32 @@ public class DriverController {
 
                 drivers.add(new Driver(protocolCode, driver.getProtocolName(), metadata.canDiscover(), configurationOptions, null));
             } catch (Exception e) {
-                e.printStackTrace();
-                // Ignore ...
+                throw new RuntimeException("Error retrieving driver list", e);
             }
         }
+        return drivers;
+    }
 
-        // Sort the list by the code of the driver elements.
-        drivers.sort(Comparator.comparing(Driver::getCode));
-
-        return ResponseEntity.ok(drivers);
+    public void discover(String protocolCode) {
+        try {
+            PlcDriver driver = driverManager.getDriver(protocolCode);
+            if (!driver.getMetadata().canDiscover()) {
+                throw new RuntimeException("Driver doesn't support discovery");
+            } else {
+                PlcDiscoveryRequest request = driver.discoveryRequestBuilder().addQuery("all", "*").build();
+                // Execute the discovery request and have all connections found be added as connections.
+                request.executeWithHandler(discoveryItem -> {
+                    Device device = new Device();
+                    applicationEventPublisher.publishEvent(new DeviceEvent(device, EventType.CREATED));
+                }).whenComplete((plcDiscoveryResponse, throwable) -> {
+                    if(throwable != null) {
+                        throw new RuntimeException("Error executing discovery", throwable);
+                    }
+                });
+            }
+        } catch (PlcConnectionException e) {
+            throw new RuntimeException("Error getting driver", e);
+        }
     }
 
 }
diff --git a/plc4j/tools/ui/backend/src/main/resources/application.properties b/plc4j/tools/ui/backend/src/main/resources/application.properties
index fe95886d5c..4909f699b8 100644
--- a/plc4j/tools/ui/backend/src/main/resources/application.properties
+++ b/plc4j/tools/ui/backend/src/main/resources/application.properties
@@ -16,3 +16,13 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+
+# Settings for the Derby sql database, which contains the application settings.
+spring.jpa.generate-ddl=true
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.database=default
+spring.datasource.url=jdbc:derby:directory:storage/settings;create=true
+
+# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
+spring.sql.init.continue-on-error=false
+spring.datasource.generate-unique-name=false
diff --git a/plc4j/tools/ui/backend/ws.html b/plc4j/tools/ui/backend/ws.html
new file mode 100644
index 0000000000..492e0ae106
--- /dev/null
+++ b/plc4j/tools/ui/backend/ws.html
@@ -0,0 +1,36 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>Profile notification client
+    </title>
+</head>
+<body>
+<script>
+  // <1>
+  var socket = new WebSocket('ws://localhost:8080/ws');
+  // <2>
+  socket.addEventListener('message', function (event) {
+    window.alert('message from server: ' + event.data);
+  });
+</script>
+</body>
+</html>
diff --git a/plc4j/tools/ui/frontend/frontend/package.json b/plc4j/tools/ui/frontend/frontend/package.json
index 85e36de331..cd38560982 100644
--- a/plc4j/tools/ui/frontend/frontend/package.json
+++ b/plc4j/tools/ui/frontend/frontend/package.json
@@ -35,5 +35,14 @@
     "eslint-plugin-react-refresh": "^0.4.4",
     "typescript": "^5.2.2",
     "vite": "^5.0.0"
+  },
+  "proxy": {
+      "/ws": {
+          "target": "ws://localhost:8080",
+          "ws": true
+      },
+      "/": {
+          "target": "http://localhost:8080"
+      }
   }
 }
diff --git a/plc4j/tools/ui/frontend/frontend/src/generated/plc4j-tools-ui-frontend.ts b/plc4j/tools/ui/frontend/frontend/src/generated/plc4j-tools-ui-frontend.ts
index 79d344b313..1c2b82183a 100644
--- a/plc4j/tools/ui/frontend/frontend/src/generated/plc4j-tools-ui-frontend.ts
+++ b/plc4j/tools/ui/frontend/frontend/src/generated/plc4j-tools-ui-frontend.ts
@@ -19,7 +19,7 @@
  */
 /* tslint:disable */
 /* eslint-disable */
-// Generated using typescript-generator version 3.2.1263 on 2023-12-26 16:21:15.
+// Generated using typescript-generator version 3.2.1263 on 2023-12-29 16:56:08.
 
 export interface PlcBrowseItem {
     name: string;
@@ -27,8 +27,8 @@ export interface PlcBrowseItem {
     writable: boolean;
     children: { [index: string]: PlcBrowseItem };
     tag: PlcTag;
-    options: { [index: string]: PlcValue };
     subscribable: boolean;
+    options: { [index: string]: PlcValue };
 }
 
 export interface PlcBrowseItemArrayInfo {
@@ -42,18 +42,18 @@ export interface PlcBrowseRequest extends PlcRequest {
 }
 
 export interface PlcBrowseResponse extends PlcResponse {
-    request: PlcBrowseRequest;
     queryNames: string[];
+    request: PlcBrowseRequest;
 }
 
 export interface PlcDiscoveryItem {
     name: string;
     attributes: { [index: string]: PlcValue };
-    options: { [index: string]: string };
-    connectionUrl: string;
-    transportUrl: string;
     protocolCode: string;
     transportCode: string;
+    transportUrl: string;
+    connectionUrl: string;
+    options: { [index: string]: string };
 }
 
 export interface PlcDiscoveryRequest extends PlcRequest {
@@ -61,8 +61,8 @@ export interface PlcDiscoveryRequest extends PlcRequest {
 
 export interface PlcDiscoveryResponse extends PlcResponse {
     responseCode: PlcResponseCode;
-    values: PlcDiscoveryItem[];
     request: PlcDiscoveryRequest;
+    values: PlcDiscoveryItem[];
 }
 
 export interface PlcConnectionMetadata {
@@ -82,17 +82,16 @@ export interface PlcQuery {
 }
 
 export interface PlcTag {
-    addressString: string;
-    arrayInfo: ArrayInfo[];
     plcValueType: PlcValueType;
+    arrayInfo: ArrayInfo[];
+    addressString: string;
 }
 
 export interface PlcValue {
     bigInteger: number;
-    boolean: boolean;
-    simple: boolean;
     short: number;
     length: number;
+    boolean: boolean;
     byte: number;
     int: number;
     long: number;
@@ -107,11 +106,12 @@ export interface PlcValue {
     duration: Duration;
     date: Date;
     keys: string[];
-    list: PlcValue[];
-    nullable: boolean;
-    metaDataNames: string[];
-    dateTime: Date;
     plcValueType: PlcValueType;
+    dateTime: Date;
+    metaDataNames: string[];
+    nullable: boolean;
+    list: PlcValue[];
+    simple: boolean;
     raw: any;
     struct: { [index: string]: PlcValue };
 }
@@ -136,6 +136,10 @@ export interface TemporalAmount {
 export interface Serializable {
 }
 
+export interface Device {
+    id: number;
+}
+
 export interface Driver {
     code: string;
     name: string;
@@ -174,6 +178,14 @@ export class RestApplicationClient<O> {
     constructor(protected httpClient: HttpClient<O>) {
     }
 
+    /**
+     * HTTP GET /api/discover/{protocolCode}
+     * Java method: org.apache.plc4x.java.tools.ui.controller.DriverController.discover
+     */
+    discover(protocolCode: string, options?: O): RestResponse<Device[]> {
+        return this.httpClient.request({ method: "GET", url: uriEncoding`api/discover/${protocolCode}`, options: options });
+    }
+
     /**
      * HTTP GET /api/drivers
      * Java method: org.apache.plc4x.java.tools.ui.controller.DriverController.getDriverList
diff --git a/plc4j/tools/ui/frontend/frontend/src/pages/Inspect.tsx b/plc4j/tools/ui/frontend/frontend/src/pages/Inspect.tsx
index e2e533ddee..fb546f8000 100644
--- a/plc4j/tools/ui/frontend/frontend/src/pages/Inspect.tsx
+++ b/plc4j/tools/ui/frontend/frontend/src/pages/Inspect.tsx
@@ -20,19 +20,22 @@ import {TabPanel, TabView} from "primereact/tabview";
 import {Splitter, SplitterPanel} from "primereact/splitter";
 import {useState} from "react";
 import {RestApplicationClient} from "../generated/plc4j-tools-ui-frontend.ts";
-import axios from "axios";
 import {TreeItemData} from "../model/TreeItemData.ts";
 import {ScrollPanel} from "primereact/scrollpanel";
 import NavigationTree from "../components/NavigationTree.tsx";
 import PlcConnection from "../components/PlcConnection.tsx";
+import axios from "axios";
 
 export default function Inspect() {
+    const [initialized, setInitialized] = useState(false)
     const [driverTreeRoots, setDriverTreeRoots] = useState<TreeItemData[]>([])
     const restClient = new RestApplicationClient(axios);
 
     function updateDriverList() {
+        console.log("updateDriverList");
         const driverList = restClient.getDriverList();
         driverList.then(response => {
+            console.log(response);
             let newDriverTreeRoots: TreeItemData[] = [];
             response.data.map(driverValue => {
                 newDriverTreeRoots = [...newDriverTreeRoots, {
@@ -51,7 +54,8 @@ export default function Inspect() {
         })
     }
 
-    if(driverTreeRoots.length == 0) {
+    if(!initialized) {
+        setInitialized(true)
         updateDriverList()
     }
 
diff --git a/plc4j/tools/ui/frontend/pom.xml b/plc4j/tools/ui/frontend/pom.xml
index 68c50a5c6d..d015b8a321 100644
--- a/plc4j/tools/ui/frontend/pom.xml
+++ b/plc4j/tools/ui/frontend/pom.xml
@@ -45,6 +45,7 @@
               <goal>generate</goal>
             </goals>
             <configuration>
+              <skip>true</skip>
               <classes>
                 <!-- These are the PLC4J API types -->
                 <class>org.apache.plc4x.java.api.messages.PlcBrowseItem</class>
diff --git a/plc4j/tools/ui/pom.xml b/plc4j/tools/ui/pom.xml
index 33630e86a6..0f27bb0715 100644
--- a/plc4j/tools/ui/pom.xml
+++ b/plc4j/tools/ui/pom.xml
@@ -35,6 +35,7 @@
 
   <properties>
     <project.build.outputTimestamp>2023-11-02T07:23:09Z</project.build.outputTimestamp>
+    <spring-boot.version>3.2.1</spring-boot.version>
   </properties>
 
   <modules>
@@ -43,4 +44,32 @@
     <module>frontend</module>
   </modules>
 
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring-boot.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>jakarta.annotation</groupId>
+        <artifactId>jakarta.annotation-api</artifactId>
+        <version>2.1.1</version>
+      </dependency>
+      <dependency>
+        <groupId>org.aspectj</groupId>
+        <artifactId>aspectjweaver</artifactId>
+        <version>1.9.20.1</version>
+      </dependency>
+      <dependency>
+        <groupId>io.projectreactor</groupId>
+        <artifactId>reactor-core</artifactId>
+        <version>3.6.1</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
 </project>
\ No newline at end of file