diff --git a/management/pom.xml b/management/pom.xml
index 8499457..e055c31 100644
--- a/management/pom.xml
+++ b/management/pom.xml
@@ -66,6 +66,11 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java b/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java
new file mode 100644
index 0000000..dbdb7e9
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java
@@ -0,0 +1,19 @@
+package eu.nebulous.resource.discovery;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Slf4j
+@Configuration
+public class ResourceDiscoveryConfig {
+ @Bean
+ public static ObjectMapper objectMapper() {
+ return new ObjectMapper()
+ .registerModule(new JavaTimeModule())
+ .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+ }
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java b/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java
index 9449c39..07b2216 100644
--- a/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java
+++ b/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java
@@ -1,6 +1,5 @@
package eu.nebulous.resource.discovery;
-import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
@@ -16,7 +15,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import static org.springframework.security.config.Customizer.withDefaults;
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java
new file mode 100644
index 0000000..3a97f7d
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java
@@ -0,0 +1,14 @@
+
+package eu.nebulous.resource.discovery.monitor;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+//@EnableAsync
+//@EnableScheduling
+@RequiredArgsConstructor
+public class DeviceProcessor {
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java
new file mode 100644
index 0000000..5b2724d
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java
@@ -0,0 +1,50 @@
+package eu.nebulous.resource.discovery.monitor.controller;
+
+import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice;
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import eu.nebulous.resource.discovery.monitor.model.DeviceException;
+import eu.nebulous.resource.discovery.monitor.service.DeviceManagementService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/monitor/archived")
+@PreAuthorize("hasAuthority('ROLE_ADMIN')")
+public class ArchivedDeviceManagementController {
+ private final DeviceManagementService deviceService;
+
+ @GetMapping(value = "/device/all", produces = MediaType.APPLICATION_JSON_VALUE)
+ public List listDevicesAll() {
+ return deviceService.getArchivedAll();
+ }
+
+ @GetMapping(value = "/device/owner/{owner}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public List listDevicesForOwner(@PathVariable String owner) {
+ return deviceService.getArchivedByOwner(owner);
+ }
+
+ @GetMapping(value = "/device/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public Device getDevice(@PathVariable String id) {
+ return deviceService.getArchivedById(id)
+ .orElseThrow(() -> new DeviceException("Not found archived device with id: "+id));
+ }
+
+ @GetMapping(value = "/device/ipaddress/{ipAddress}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public List getDeviceByIpAddress(@PathVariable String ipAddress) {
+ return deviceService.getArchivedByIpAddress(ipAddress);
+ }
+
+ @GetMapping(value = "/device/{id}/unarchive", produces = MediaType.APPLICATION_JSON_VALUE)
+ public String unarchiveDevice(@PathVariable String id) {
+ deviceService.unarchiveDevice(id);
+ return "UNARCHIVED";
+ }
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java
new file mode 100644
index 0000000..fa06898
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java
@@ -0,0 +1,72 @@
+package eu.nebulous.resource.discovery.monitor.controller;
+
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import eu.nebulous.resource.discovery.monitor.model.DeviceException;
+import eu.nebulous.resource.discovery.monitor.service.DeviceConversionService;
+import eu.nebulous.resource.discovery.monitor.service.DeviceManagementService;
+import eu.nebulous.resource.discovery.registration.IRegistrationRequestProcessor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/monitor")
+@PreAuthorize("hasAuthority('ROLE_ADMIN')")
+public class DeviceManagementController {
+ private final DeviceManagementService deviceService;
+ private final DeviceConversionService deviceConversionService;
+ private final IRegistrationRequestProcessor deviceRequestProcessor;
+
+ @GetMapping(value = "/device/all", produces = MediaType.APPLICATION_JSON_VALUE)
+ public List listDevicesAll() {
+ return deviceService.getAll();
+ }
+
+ @GetMapping(value = "/device/owner/{owner}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public List listDevicesForOwner(@PathVariable String owner) {
+ return deviceService.getByOwner(owner);
+ }
+
+ @GetMapping(value = "/device/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public Device getDevice(@PathVariable String id) {
+ return deviceService.getById(id)
+ .orElseThrow(() -> new DeviceException("Not found device with id: "+id));
+ }
+
+ @GetMapping(value = "/device/ipaddress/{ipAddress}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public Device getDeviceByIpAddress(@PathVariable String ipAddress) {
+ return deviceService.getByIpAddress(ipAddress)
+ .orElseThrow(() -> new DeviceException("Not found device with IP address: "+ipAddress));
+ }
+
+ @PutMapping(value = "/device", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+ public Device createDevice(@RequestBody Device device) {
+ return deviceService.save(device);
+ }
+
+ @PostMapping(value = "/device/{id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+ public Device updateDevice(@PathVariable String id, @RequestBody Device device) {
+ if (! StringUtils.equals(id, device.getId()))
+ throw new DeviceException(
+ "Id does not match the id in device: "+id+" <> "+device.getId());
+ return deviceService.update(device);
+ }
+
+ @DeleteMapping(value = "/device/{id}")
+ public void deleteDevice(@PathVariable String id) {
+ deviceService.deleteById(id);
+ }
+
+ @GetMapping(value = "/device/{id}/archive", produces = MediaType.APPLICATION_JSON_VALUE)
+ public String archiveDevice(@PathVariable String id) {
+ deviceService.archiveDevice(id);
+ return "ARCHIVED";
+ }
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java
new file mode 100644
index 0000000..c91c84d
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java
@@ -0,0 +1,9 @@
+package eu.nebulous.resource.discovery.monitor.model;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Slf4j
+@Document(collection = "archived_device")
+public class ArchivedDevice extends Device {
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java
new file mode 100644
index 0000000..187fad4
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java
@@ -0,0 +1,41 @@
+package eu.nebulous.resource.discovery.monitor.model;
+
+import eu.nebulous.resource.discovery.registration.model.RegistrationRequest;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.SuperBuilder;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@Document(collection = "device")
+public class Device {
+ private String id;
+ private String os;
+ private String name;
+ private String owner;
+ private String ipAddress;
+ private String username;
+ private char[] password;
+ private char[] publicKey;
+ private Map deviceInfo;
+
+ private RegistrationRequest request;
+ private String requestId;
+ private Instant creationDate;
+ private Instant lastUpdateDate;
+ private Instant archiveDate;
+ private DeviceStatus status;
+
+ private String nodeReference;
+ @Setter(AccessLevel.NONE)
+ private List messages = new ArrayList<>();
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java
new file mode 100644
index 0000000..9db438d
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java
@@ -0,0 +1,8 @@
+
+package eu.nebulous.resource.discovery.monitor.model;
+
+public class DeviceException extends RuntimeException {
+ public DeviceException(String message) { super(message); }
+ public DeviceException(Throwable t) { super(t); }
+ public DeviceException(String message, Throwable t) { super(message, t); }
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java
new file mode 100644
index 0000000..1b45cf7
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java
@@ -0,0 +1,8 @@
+package eu.nebulous.resource.discovery.monitor.model;
+
+public enum DeviceStatus {
+ NEW_DEVICE, ON_HOLD,
+ ONBOARDING, ONBOARDED, ONBOARD_ERROR,
+ HEALTHY, BUSY, IDLE,
+ OFFBOARDING, OFFBOARDED, OFFBOARD_ERROR
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java
new file mode 100644
index 0000000..001b89e
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java
@@ -0,0 +1,14 @@
+package eu.nebulous.resource.discovery.monitor.repository;
+
+import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice;
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import eu.nebulous.resource.discovery.registration.model.RegistrationRequest;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface ArchivedDeviceRepository extends MongoRepository {
+ List findByOwner(String owner);
+ List findByIpAddress(String ipAddress);
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java
new file mode 100644
index 0000000..da30f95
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java
@@ -0,0 +1,12 @@
+package eu.nebulous.resource.discovery.monitor.repository;
+
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface DeviceRepository extends MongoRepository {
+ List findByOwner(String owner);
+ Optional findByIpAddress(String ipAddress);
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java
new file mode 100644
index 0000000..43532e2
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java
@@ -0,0 +1,26 @@
+package eu.nebulous.resource.discovery.monitor.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice;
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DeviceConversionService {
+ private final ObjectMapper objectMapper;
+
+ public ArchivedDevice toArchivedDevice(@NonNull Device device) {
+ return objectMapper
+ .convertValue(device, ArchivedDevice.class);
+ }
+
+ public Device toDevice(@NonNull ArchivedDevice archivedDevice) {
+ return objectMapper
+ .convertValue(archivedDevice, Device.class);
+ }
+}
diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java
new file mode 100644
index 0000000..3955c3f
--- /dev/null
+++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java
@@ -0,0 +1,154 @@
+package eu.nebulous.resource.discovery.monitor.service;
+
+import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice;
+import eu.nebulous.resource.discovery.monitor.model.Device;
+import eu.nebulous.resource.discovery.monitor.model.DeviceException;
+import eu.nebulous.resource.discovery.monitor.model.DeviceStatus;
+import eu.nebulous.resource.discovery.monitor.repository.ArchivedDeviceRepository;
+import eu.nebulous.resource.discovery.monitor.repository.DeviceRepository;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.*;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DeviceManagementService {
+ private final DeviceRepository deviceRepository;
+ private final ArchivedDeviceRepository archivedDeviceRepository;
+ private final DeviceConversionService deviceConversionService;
+
+ // ------------------------------------------------------------------------
+
+ public List getAll() {
+ return Collections.unmodifiableList(deviceRepository.findAll());
+ }
+
+ public List getByOwner(@NonNull String owner) {
+ return deviceRepository.findByOwner(owner);
+ }
+
+ public Optional getById(@NonNull String id) {
+ return deviceRepository.findById(id);
+ }
+
+ public Optional getByIpAddress(@NonNull String ipAddress) {
+ return deviceRepository.findByIpAddress(ipAddress);
+ }
+
+ public @NonNull Device save(@NonNull Device device) {
+ DeviceStatus status = device.getStatus();
+ if (status == null) {
+ device.setStatus(DeviceStatus.NEW_DEVICE);
+ }
+ if (status != null && status != DeviceStatus.NEW_DEVICE) {
+ throw new DeviceException("Cannot save a new device with status "+status);
+ }
+ if (StringUtils.isBlank(device.getId())) {
+ device.setId( UUID.randomUUID().toString() );
+ } else {
+ throw new DeviceException(
+ "New device already has an Id: " + device.getId());
+ }
+ if (getById(device.getOs()).isPresent())
+ throw new DeviceException(
+ "A device with the same Id already exists in repository: "+device.getId());
+ if (getByIpAddress(device.getIpAddress()).isPresent())
+ throw new DeviceException(
+ "A device with the same IP address already exists in repository: "+device.getIpAddress());
+ device.setCreationDate(Instant.now());
+ checkDevice(device);
+
+ deviceRepository.save(device);
+ return device;
+ }
+
+ public Device update(@NonNull Device device) {
+ Optional result = getById(device.getId());
+ if (result.isEmpty())
+ throw new DeviceException(
+ "Device with the Id does not exists in repository: "+device.getId());
+ checkDevice(device);
+
+ device.setLastUpdateDate(Instant.now());
+ deviceRepository.save(device);
+
+ return getById(device.getId()).orElseThrow(() ->
+ new DeviceException("Device update failed for Device Id: "+device.getId()));
+ }
+
+ private void checkDevice(@NonNull Device device) {
+ List errors = new ArrayList<>();
+ if (StringUtils.isBlank(device.getId())) errors.add("Null or blank Id");
+ if (StringUtils.isBlank(device.getOwner())) errors.add("Null or blank Owner");
+ if (device.getCreationDate()==null) errors.add("Null Creation date");
+ if (device.getStatus()==null) errors.add("Null Status");
+ if (!errors.isEmpty()) {
+ throw new DeviceException(
+ String.format("Device spec has errors: %s\n%s",
+ String.join(", ", errors), device));
+ }
+ }
+
+ public void deleteById(@NonNull String id) {
+ Optional result = getById(id);
+ if (result.isEmpty())
+ throw new DeviceException(
+ "Device with the Id does not exists in repository: "+id);
+ deviceRepository.delete(result.get());
+ result.get().setLastUpdateDate(Instant.now());
+ }
+
+ public void delete(@NonNull Device device) {
+ deviceRepository.deleteById(device.getId());
+ device.setLastUpdateDate(Instant.now());
+ }
+
+ // ------------------------------------------------------------------------
+
+ public List getArchivedAll() {
+ return Collections.unmodifiableList(archivedDeviceRepository.findAll());
+ }
+
+ public List getArchivedByOwner(@NonNull String owner) {
+ return archivedDeviceRepository.findByOwner(owner);
+ }
+
+ public Optional getArchivedById(@NonNull String id) {
+ return archivedDeviceRepository.findById(id);
+ }
+
+ public List getArchivedByIpAddress(@NonNull String ipAddress) {
+ return archivedDeviceRepository.findByIpAddress(ipAddress);
+ }
+
+ public void archiveDevice(String id) {
+ archiveRequestBySystem(id);
+ }
+
+ public void archiveRequestBySystem(String id) {
+ Optional result = getById(id);
+ if (result.isEmpty())
+ throw new DeviceException(
+ "Device with the Id does not exists in repository: " + id);
+ result.get().setArchiveDate(Instant.now());
+ archivedDeviceRepository.save(deviceConversionService.toArchivedDevice(result.get()));
+ deviceRepository.delete(result.get());
+ }
+
+ public void unarchiveDevice(String id) {
+ Optional result = getArchivedById(id);
+ if (result.isEmpty())
+ throw new DeviceException(
+ "Archived device with Id does not exists in repository: "+id);
+
+ result.get().setArchiveDate(null);
+ deviceRepository.save(deviceConversionService.toDevice(result.get()));
+ archivedDeviceRepository.deleteById(result.get().getId());
+ }
+}