Kaynağa Gözat

Added starting time, change data provider to new senslog endpoint

Lukas Cerny 4 yıl önce
ebeveyn
işleme
f27927199e

+ 4 - 3
config/foodie.yaml

@@ -1,6 +1,9 @@
+general:
+  firstStartAt: "00:20:00" # hh:mm:ss
+
 emailServers:
   lspEmail:
-    smtpHost: "mail.lesprojekt.cz"
+    smtpHost: "10.0.0.100" # "mail.lesprojekt.cz"
     smtpPort: 465
     authUsername: "watchdog@senslog.org"
     authPassword: "5jspdD"
@@ -19,7 +22,6 @@ dataProviders:
     type: WEB_SERVICE
     config:
       baseUrl: "http://sensor.lesprojekt.cz/senslog15"
-      userName: "kynsperk"
       groupName: "kynsperk"
       auth:
         username: "watchdog"
@@ -29,7 +31,6 @@ dataProviders:
     type: WEB_SERVICE
     config:
       baseUrl: "http://sensor.lesprojekt.cz/senslog15"
-      userName: "mikrsoil"
       groupName: "rostenice_pudni"
       auth:
         username: "watchdog"

+ 95 - 97
src/main/java/cz/senslog/watchdog/app/Application.java

@@ -1,98 +1,96 @@
-package cz.senslog.watchdog.app;
-
-
-import cz.senslog.watchdog.config.Configuration;
-import cz.senslog.watchdog.config.ExecutableGroup;
-import cz.senslog.watchdog.messagebroker.MessageBroker;
-import cz.senslog.watchdog.messagebroker.MessageBrokerManager;
-import cz.senslog.watchdog.provider.DataProvider;
-import cz.senslog.watchdog.provider.DataProviderManager;
-import cz.senslog.watchdog.util.DateTrunc;
-import cz.senslog.watchdog.util.schedule.Scheduler;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.io.IOException;
-import java.time.LocalDateTime;
-
-import static cz.senslog.watchdog.util.DateTrunc.trunc;
-
-public class Application extends Thread {
-
-    private static final Logger logger = LogManager.getLogger(Application.class);
-
-    private final Parameters params;
-
-    static Thread init(String... args) throws IOException {
-        Parameters parameters = Parameters.parse(args);
-
-        if (parameters.isHelp()) {
-            return new Thread(parameters::printHelp);
-        }
-
-        Application app = new Application(parameters);
-        Runtime.getRuntime().addShutdownHook(new Thread(app::interrupt, "clean-app"));
-
-        return app;
-    }
-
-    private Application(Parameters parameters) {
-        super("app");
-        this.params = parameters;
-    }
-
-    @Override
-    public void interrupt() {}
-
-    @Override
-    public void run() {
-
-        String configFilePath = params.getConfigFileName();
-        Configuration config = null;
-        try {
-            config = Configuration.load(configFilePath);
-        } catch (IOException e) {
-            logger.catching(e);
-            System.exit(1);
-        }
-
-        MessageBrokerManager messageBrokerManager = MessageBrokerManager.createInstance(
-                config.getEmailServerConfigs(), config.getMessageBrokerConfigs()
-        );
-
-        DataProviderManager dataProviderManager = DataProviderManager.createInstance(
-                config.getDataProviderConfigs()
-        );
-
-        // config.getExecutableGroups().forEach(System.out::println);
-
-
-        LocalDateTime now = LocalDateTime.now();
-        Scheduler.SchedulerBuilder schedulerBuilder = Scheduler.createBuilder();
-        for (ExecutableGroup exeGroup : config.getExecutableGroups()) {
-            MessageBroker msgBroker = messageBrokerManager.getInstance(exeGroup.getConfig().getMessageBrokerId());
-            DataProvider dataProvider = dataProviderManager.getInstance(exeGroup.getConfig().getDataProviderId());
-
-            Watcher watcher = Watcher.create(exeGroup, dataProvider, msgBroker);
-            Integer period = exeGroup.getConfig().getPeriod();
-            if (period == null) {
-                throw new IllegalStateException("Period for the group '"+exeGroup.getConfig().getId()+"' is not specified.");
-            }
-
-            String taskName = exeGroup.getConfig().getId();
-            if (params.isExecuteImmediately()) {
-                schedulerBuilder.addTask(taskName, watcher::check);
-            } else {
-                schedulerBuilder.addTask(taskName, watcher::check, period, trunc(now, period).plusSeconds(period));
-            }
-        }
-
-        // TODO super groups
-
-        Scheduler scheduler = schedulerBuilder.build();
-
-        scheduler.start();
-
-
-    }
+package cz.senslog.watchdog.app;
+
+import cz.senslog.watchdog.config.Configuration;
+import cz.senslog.watchdog.config.ExecutableGroup;
+import cz.senslog.watchdog.messagebroker.MessageBroker;
+import cz.senslog.watchdog.messagebroker.MessageBrokerManager;
+import cz.senslog.watchdog.provider.DataProvider;
+import cz.senslog.watchdog.provider.DataProviderManager;
+import cz.senslog.watchdog.util.schedule.Scheduler;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+
+public class Application extends Thread {
+
+    private static final Logger logger = LogManager.getLogger(Application.class);
+
+    private final Parameters params;
+
+    static Thread init(String... args) throws IOException {
+        Parameters parameters = Parameters.parse(args);
+
+        if (parameters.isHelp()) {
+            return new Thread(parameters::printHelp);
+        }
+
+        Application app = new Application(parameters);
+        Runtime.getRuntime().addShutdownHook(new Thread(app::interrupt, "clean-app"));
+
+        return app;
+    }
+
+    private Application(Parameters parameters) {
+        super("app");
+        this.params = parameters;
+    }
+
+    @Override
+    public void interrupt() {}
+
+    @Override
+    public void run() {
+
+        String configFilePath = params.getConfigFileName();
+        Configuration config = null;
+        try {
+            config = Configuration.load(configFilePath);
+        } catch (IOException e) {
+            logger.catching(e);
+            System.exit(1);
+        }
+
+        MessageBrokerManager messageBrokerManager = MessageBrokerManager.createInstance(
+                config.getEmailServerConfigs(), config.getMessageBrokerConfigs()
+        );
+
+        DataProviderManager dataProviderManager = DataProviderManager.createInstance(
+                config.getDataProviderConfigs()
+        );
+
+        LocalDateTime now = LocalDateTime.now();
+        LocalTime startAtTime = config.getGeneralConfig().getFirstStartAt();
+        LocalDate startAtDate = now.toLocalTime().isBefore(startAtTime) ? now.toLocalDate() : now.toLocalDate().plusDays(1);
+        LocalDateTime startAt = LocalDateTime.of(startAtDate, startAtTime);
+        Scheduler.SchedulerBuilder schedulerBuilder = Scheduler.createBuilder();
+        for (ExecutableGroup exeGroup : config.getExecutableGroups()) {
+            MessageBroker msgBroker = messageBrokerManager.getInstance(exeGroup.getConfig().getMessageBrokerId());
+            DataProvider dataProvider = dataProviderManager.getInstance(exeGroup.getConfig().getDataProviderId());
+
+            Watcher watcher = Watcher.create(exeGroup, dataProvider, msgBroker);
+            Integer period = exeGroup.getConfig().getPeriod();
+            if (period == null) {
+                throw new IllegalStateException("Period for the group '"+exeGroup.getConfig().getId()+"' is not specified.");
+            }
+
+            String taskName = exeGroup.getConfig().getId();
+            if (params.isExecuteImmediately()) {
+                schedulerBuilder.addTask(taskName, watcher::check);
+            } else {
+             //   schedulerBuilder.addTask(taskName, watcher::check, period, trunc(now, period).plusSeconds(period));
+                schedulerBuilder.addTask(taskName, watcher::check, period, startAt);
+            }
+        }
+
+        // TODO super groups
+
+        Scheduler scheduler = schedulerBuilder.build();
+
+        scheduler.start();
+    }
 }

+ 372 - 360
src/main/java/cz/senslog/watchdog/config/Configuration.java

@@ -1,360 +1,372 @@
-package cz.senslog.watchdog.config;
-
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.yaml.snakeyaml.Yaml;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.*;
-
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-
-public class Configuration {
-
-    private static final Logger logger = LogManager.getLogger(Configuration.class);
-
-    private final Collection<ExecutableGroup> executableGroups;
-    private final Collection<MessageBrokerConfig> messageBrokerConfigs;
-    private final Collection<EmailServerConfig> emailServerConfigs;
-    private final Collection<DataProviderConfig> dataProviderConfigs;
-
-    private Configuration(
-            Collection<ExecutableGroup> executableGroups,
-            Collection<EmailServerConfig> emailServerConfigs,
-            Collection<MessageBrokerConfig> messageBrokerConfigs,
-            Collection<DataProviderConfig> dataProviderConfigs
-    ){
-        this.executableGroups = executableGroups;
-        this.messageBrokerConfigs = messageBrokerConfigs;
-        this.emailServerConfigs = emailServerConfigs;
-        this.dataProviderConfigs = dataProviderConfigs;
-    }
-
-    private static Configuration make(
-            List<TempUnitConfig> monitoredObjects,
-            Map<String, EmailServerConfig> emailServers,
-            Map<String, MessageBrokerConfig> messageBrokers,
-            Map<String, DataProviderConfig> dataProviders,
-            Map<String, GroupConfig> groups,
-            Map<String, SuperGroupConfig> superGroups
-    ) {
-
-        Map<String, ExecutableGroup> executableGroupsMap = new HashMap<>();
-        for (TempUnitConfig mObject : monitoredObjects) {
-            Set<String> unitGroups = mObject.groups;
-            List<TempSensorConfig> sensors = mObject.sensors;
-            if (unitGroups.isEmpty() && sensors.isEmpty()) {
-                throw new IllegalStateException("The monitored object '"+mObject.id+"' is not assigned to any groups.");
-            }
-            sensors = !sensors.isEmpty() ? sensors : singletonList(new TempAllSensorsConfig(mObject.period));
-            for (TempSensorConfig sensor : sensors) {
-                Set<String> sensorGroups = sensor.groups;
-                if (unitGroups.isEmpty() && sensorGroups.isEmpty()) {
-                    throw new IllegalStateException("The monitored object '"+sensor.id+"' is not assigned to any groups.");
-                }
-                sensorGroups.addAll(unitGroups);
-                for (String groupId : sensorGroups) {
-                    GroupConfig groupConfig = groups.get(groupId);
-                    if (groupConfig == null) {
-                        throw new IllegalStateException(
-                                "Assigned group '"+groupId+"' to the monitored object '"+sensor.id+"' does not exist."
-                        );
-                    }
-                    Integer period = groupConfig.getPeriod() != null ? groupConfig.getPeriod() : mObject.period;
-                    period = period != null ? period : sensor.period;
-                    if (period == null) {
-                        throw new IllegalStateException(
-                                "The monitored object '"+sensor.id+"' does not contain a valid 'period' value."
-                        );
-                    }
-
-                    if (!dataProviders.containsKey(groupConfig.getDataProviderId())) {
-                        throw new IllegalStateException(
-                                "Assigned data provider '"+groupConfig.getDataProviderId()+"' does not exist."
-                        );
-                    }
-
-                    if (!messageBrokers.containsKey(groupConfig.getMessageBrokerId())) {
-                        throw new IllegalStateException(
-                                "Assigned message broker '"+groupConfig.getMessageBrokerId()+"' does not exist."
-                        );
-                    }
-
-                    ExecutableGroup executableGroup = executableGroupsMap.computeIfAbsent(
-                            groupId, k -> new ExecutableGroup(groupConfig)
-                    );
-
-                    MonitoredObject sensorMObj = sensor.id != null ? new MonitoredObject(sensor.id, period) : new AllMonitoredObjects(period);
-
-                    executableGroup.swapPeriod(sensorMObj.getPeriod());
-                    executableGroup.computeIfAbsent(mObject.id, MonitoredObject::new)
-                            .addNode(sensorMObj.getId(), sensorMObj);
-                }
-            }
-        }
-
-        for (Map.Entry<String, GroupConfig> groupEntry : groups.entrySet()) {
-            String groupId = groupEntry.getKey();
-            GroupConfig groupConfig = groupEntry.getValue();
-            if (!executableGroupsMap.containsKey(groupId)) {
-                MonitoredObject allSensors = new AllMonitoredObjects(groupConfig.getPeriod());
-                executableGroupsMap.computeIfAbsent(groupId, k -> new ExecutableGroup(groupConfig))
-                        .computeIfAbsent(groupId, k -> new AllMonitoredObjects(groupConfig.getPeriod()))
-                        .addNode(allSensors.getId(), allSensors);
-            }
-        }
-
-        Map<String, SuperGroup> executableSGroupsMap = new HashMap<>();
-        for (SuperGroupConfig sGConfig : superGroups.values()) {
-            if (sGConfig.getPeriod() == null) {
-                throw new IllegalStateException(
-                        "The monitored object '"+sGConfig.getId()+"' does not contain a valid 'period' value."
-                );
-            }
-
-            SuperGroup superGroup = new SuperGroup(sGConfig);
-            for (String groupId : sGConfig.getGroups()) {
-                ExecutableGroup group = executableGroupsMap.get(groupId);
-                if (group == null) {
-                    throw new IllegalStateException("Assigned group '"+groupId+"' does not exist.");
-                }
-                superGroup.addNode(groupId, group);
-            }
-            executableSGroupsMap.put(sGConfig.getId(), superGroup);
-        }
-
-        // TODO return
-        Collection<ExecutableGroup> execGroups = executableGroupsMap.values();
-        Collection<SuperGroup> execSGroups = executableSGroupsMap.values();
-        Collection<EmailServerConfig> emailServerConfigs = emailServers.values();
-        Collection<DataProviderConfig> dataProviderConfigs = dataProviders.values();
-        Collection<MessageBrokerConfig> messageBrokerConfigs = messageBrokers.values();
-
-        return new Configuration(
-                execGroups, emailServerConfigs, messageBrokerConfigs, dataProviderConfigs
-        );
-    }
-
-    public static Configuration load(String fileName) throws IOException {
-
-        logger.info("Loading '{}' configuration file.", fileName);
-
-        if (!fileName.toLowerCase().endsWith(".yaml")) {
-            throw new IllegalArgumentException(fileName + "does not contain .yaml extension.");
-        }
-
-        Path filePath = Paths.get(fileName);
-        if (Files.notExists(filePath)) {
-            throw new FileNotFoundException(fileName + " does not exist");
-        }
-
-        Map<Object, Object> properties;
-
-        logger.debug("Opening the file '{}'.", fileName);
-        try (InputStream fileStream = Files.newInputStream(filePath)) {
-            logger.debug("Parsing the yaml file '{}'.", fileName);
-            properties = new Yaml().load(fileStream);
-            logger.debug("The configuration yaml file '{}' was parsed successfully.", fileName);
-        }
-
-        if (properties == null || properties.isEmpty()) {
-            throw new IOException(String.format(
-                    "The configuration yaml file %s is empty or was not loaded successfully. ", fileName
-            ));
-        }
-
-        try {
-            return make(
-                    parseMonitoredObjects(createPropertyConfig(properties, "monitoredObjects")),
-                    parseEmailServers(createPropertyConfig(properties, "emailServers")),
-                    parseMessageBrokers(createPropertyConfig(properties, "messageBrokers")),
-                    parseDataProviders(createPropertyConfig(properties, "dataProviders")),
-                    parseGroups(createPropertyConfig(properties, "groups")),
-                    parseSuperGroups(createPropertyConfig(properties, "superGroups"))
-            );
-        } catch (IOException e) {
-            throw new IOException(String.format(
-                    "Configuration file '%s' contains an error at '%s' attribute.", fileName, e.getMessage()
-            ));
-        }
-    }
-
-    private static Map<String, EmailServerConfig> parseEmailServers(PropertyConfig config) {
-        Set<String> serverIds = config.getAttributes();
-        Map<String, EmailServerConfig> emailServers = new HashMap<>();
-        for (String id : serverIds) {
-            PropertyConfig serverConfig = config.getPropertyConfig(id);
-            emailServers.put(id, new EmailServerConfig(id,
-               serverConfig.getStringProperty("smtpHost"),
-               serverConfig.getIntegerProperty("smtpPort"),
-               serverConfig.getStringProperty("authUsername"),
-               serverConfig.getStringProperty("authPassword")
-            ));
-        }
-        return emailServers;
-    }
-
-    private static Map<String, MessageBrokerConfig> parseMessageBrokers(PropertyConfig config) {
-        Set<String> brokerIds = config.getAttributes();
-        Map<String, MessageBrokerConfig> messageBrokers = new HashMap<>(brokerIds.size());
-        for (String id : brokerIds) {
-            PropertyConfig brokerConfig = config.getPropertyConfig(id);
-            MessageBrokerType type = MessageBrokerType.of(brokerConfig.getStringProperty("type"));
-            messageBrokers.put(id, type.createConfig(id, brokerConfig.getPropertyConfig("config"))
-            );
-        }
-        return messageBrokers;
-    }
-
-    private static Map<String, DataProviderConfig> parseDataProviders(PropertyConfig config) {
-        Set<String> providerIds = config.getAttributes();
-        Map<String, DataProviderConfig> dataProviders = new HashMap<>(providerIds.size());
-        for (String id : providerIds) {
-            PropertyConfig providerConfig = config.getPropertyConfig(id);
-            DataProviderType type = DataProviderType.of(providerConfig.getStringProperty("type"));
-            dataProviders.put(id, type.createConfig(id, providerConfig.getPropertyConfig("config")));
-        }
-        return dataProviders;
-    }
-
-    private static Map<String, GroupConfig> parseGroups(PropertyConfig config) {
-        Set<String> groupIds = config.getAttributes();
-        Map<String, GroupConfig> groups = new HashMap<>(groupIds.size());
-        for (String id : groupIds) {
-            PropertyConfig groupConfig = config.getPropertyConfig(id);
-            groups.put(id, new GroupConfig(id,
-                    groupConfig.getStringProperty("name"),
-                    groupConfig.getStringProperty("dataProvider"),
-                    groupConfig.getStringProperty("messageBroker"),
-                    ResultType.of(groupConfig.getStringProperty("resultType")),
-                    groupConfig.getOptionalProperty("period", Integer.class).orElse(null)
-            ));
-        }
-        return groups;
-    }
-
-    private static Map<String, SuperGroupConfig> parseSuperGroups(PropertyConfig config) {
-        Set<String> groupIds = config.getAttributes();
-        Map<String, SuperGroupConfig> groups = new HashMap<>(groupIds.size());
-        for (String id : groupIds) {
-            PropertyConfig groupConfig = config.getPropertyConfig(id);
-
-            List<String> subGroupList = groupConfig.getArrayPropertyOf("groups", String.class);
-            Set<String> subGroupNames = new HashSet<>(subGroupList);
-            groups.put(id, new SuperGroupConfig(id,
-                    groupConfig.getStringProperty("name"),
-                    groupConfig.getStringProperty("messageBroker"),
-                    ResultType.of(groupConfig.getStringProperty("resultType")),
-                    groupConfig.getOptionalProperty("period", Integer.class).orElse(null),
-                    subGroupNames
-            ));
-        }
-        return groups;
-    }
-
-    private static List<TempUnitConfig> parseMonitoredObjects(PropertyConfig config) {
-        Set<String> unitIds = config.getAttributes();
-        List<TempUnitConfig> unitConfigs = new ArrayList<>(unitIds.size());
-        for (String unitId : unitIds) {
-            PropertyConfig unitConfig = config.getPropertyConfig(unitId);
-            Integer unitPeriod = unitConfig.getOptionalProperty("period", Integer.class).orElse(null);
-            List<String> unitGroupList = unitConfig.containsProperty("groups") ? unitConfig.getArrayPropertyOf("groups", String.class) : null;
-            Set<String> unitGroups = unitGroupList != null ? new HashSet<>(unitGroupList) : new HashSet<>();
-            List<TempSensorConfig> sensorConfigList = new ArrayList<>();
-            if (unitConfig.containsProperty("sensors")) {
-                Object sensorsObj = unitConfig.getProperty("sensors");
-                if (sensorsObj instanceof List) {
-                    List<Integer> sensors = unitConfig.getArrayPropertyOf("sensors", Integer.class);
-                    for (Integer sensorId : sensors) {
-                        sensorConfigList.add(new TempSensorConfig(Integer.toString(sensorId), unitPeriod, new HashSet<>()));
-                    }
-                } else if (sensorsObj instanceof Map) {
-                    PropertyConfig sensorsConfig = unitConfig.getPropertyConfig("sensors");
-                    Set<String> sensorIds = sensorsConfig.getAttributes();
-                    for (String sensorId : sensorIds) {
-                        PropertyConfig sensorConfig = sensorsConfig.getPropertyConfig(sensorId);
-                        Integer sensorPeriod = sensorConfig.getOptionalProperty("period", Integer.class).orElse(unitPeriod);
-                        List<String> sensorGroupList = sensorConfig.containsProperty("groups") ? sensorConfig.getArrayPropertyOf("groups", String.class) : emptyList();
-                        sensorConfigList.add(new TempSensorConfig(sensorId, sensorPeriod, new HashSet<>(sensorGroupList)));
-                    }
-                }
-            }
-            unitConfigs.add(new TempUnitConfig(unitId, unitPeriod, sensorConfigList, unitGroups));
-        }
-        return unitConfigs;
-    }
-
-    private static PropertyConfig createPropertyConfig(Map<Object, Object> properties, String propertyName) throws IOException {
-        Object generalConfig = properties.get(propertyName);
-        if (generalConfig == null) {
-            return new PropertyConfig(propertyName);
-        }
-        if (!(generalConfig instanceof Map)) {
-            throw new IOException(propertyName);
-        }
-
-        Map<?, ?> generalConfigMap = (Map<?, ?>) generalConfig;
-        PropertyConfig propertyConfig = new PropertyConfig(propertyName);
-
-        for (Map.Entry<?, ?> entry : generalConfigMap.entrySet()) {
-            String keyName = entry.getKey().toString();
-            propertyConfig.setProperty(keyName, entry.getValue());
-        }
-        return propertyConfig;
-    }
-
-    public Collection<MessageBrokerConfig> getMessageBrokerConfigs() {
-        return messageBrokerConfigs;
-    }
-
-    public Collection<EmailServerConfig> getEmailServerConfigs() {
-        return emailServerConfigs;
-    }
-
-    public Collection<DataProviderConfig> getDataProviderConfigs() {
-        return dataProviderConfigs;
-    }
-
-    public Collection<ExecutableGroup> getExecutableGroups() {
-        return executableGroups;
-    }
-
-    private static class TempUnitConfig {
-        private final String id;
-        private final Integer period;
-        private final List<TempSensorConfig> sensors;
-        private final Set<String> groups;
-
-        private TempUnitConfig(String id, Integer period, List<TempSensorConfig> sensors, Set<String> groups) {
-            this.id = id;
-            this.period = period;
-            this.sensors = sensors;
-            this.groups = groups;
-        }
-    }
-
-    private static class TempSensorConfig {
-        private final String id;
-        private final Integer period;
-        private final Set<String> groups;
-
-        private TempSensorConfig(String id, Integer period, Set<String> groups) {
-            this.id = id;
-            this.period = period;
-            this.groups = groups;
-        }
-    }
-
-    private static class TempAllSensorsConfig extends TempSensorConfig {
-        private TempAllSensorsConfig(Integer period) {
-            super(null, period, new HashSet<>());
-        }
-    }
-}
+package cz.senslog.watchdog.config;
+
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+public class Configuration {
+
+    private static final Logger logger = LogManager.getLogger(Configuration.class);
+
+    private final GeneralConfig generalConfig;
+    private final Collection<ExecutableGroup> executableGroups;
+    private final Collection<MessageBrokerConfig> messageBrokerConfigs;
+    private final Collection<EmailServerConfig> emailServerConfigs;
+    private final Collection<DataProviderConfig> dataProviderConfigs;
+
+    private Configuration(
+            GeneralConfig generalConfig,
+            Collection<ExecutableGroup> executableGroups,
+            Collection<EmailServerConfig> emailServerConfigs,
+            Collection<MessageBrokerConfig> messageBrokerConfigs,
+            Collection<DataProviderConfig> dataProviderConfigs
+    ){
+        this.generalConfig = generalConfig;
+        this.executableGroups = executableGroups;
+        this.messageBrokerConfigs = messageBrokerConfigs;
+        this.emailServerConfigs = emailServerConfigs;
+        this.dataProviderConfigs = dataProviderConfigs;
+    }
+
+    private static Configuration make(
+            GeneralConfig generalConfig,
+            List<TempUnitConfig> monitoredObjects,
+            Map<String, EmailServerConfig> emailServers,
+            Map<String, MessageBrokerConfig> messageBrokers,
+            Map<String, DataProviderConfig> dataProviders,
+            Map<String, GroupConfig> groups,
+            Map<String, SuperGroupConfig> superGroups
+    ) {
+
+        Map<String, ExecutableGroup> executableGroupsMap = new HashMap<>();
+        for (TempUnitConfig mObject : monitoredObjects) {
+            Set<String> unitGroups = mObject.groups;
+            List<TempSensorConfig> sensors = mObject.sensors;
+            if (unitGroups.isEmpty() && sensors.isEmpty()) {
+                throw new IllegalStateException("The monitored object '"+mObject.id+"' is not assigned to any groups.");
+            }
+            sensors = !sensors.isEmpty() ? sensors : singletonList(new TempAllSensorsConfig(mObject.period));
+            for (TempSensorConfig sensor : sensors) {
+                Set<String> sensorGroups = sensor.groups;
+                if (unitGroups.isEmpty() && sensorGroups.isEmpty()) {
+                    throw new IllegalStateException("The monitored object '"+sensor.id+"' is not assigned to any groups.");
+                }
+                sensorGroups.addAll(unitGroups);
+                for (String groupId : sensorGroups) {
+                    GroupConfig groupConfig = groups.get(groupId);
+                    if (groupConfig == null) {
+                        throw new IllegalStateException(
+                                "Assigned group '"+groupId+"' to the monitored object '"+sensor.id+"' does not exist."
+                        );
+                    }
+                    Integer period = groupConfig.getPeriod() != null ? groupConfig.getPeriod() : mObject.period;
+                    period = period != null ? period : sensor.period;
+                    if (period == null) {
+                        throw new IllegalStateException(
+                                "The monitored object '"+sensor.id+"' does not contain a valid 'period' value."
+                        );
+                    }
+
+                    if (!dataProviders.containsKey(groupConfig.getDataProviderId())) {
+                        throw new IllegalStateException(
+                                "Assigned data provider '"+groupConfig.getDataProviderId()+"' does not exist."
+                        );
+                    }
+
+                    if (!messageBrokers.containsKey(groupConfig.getMessageBrokerId())) {
+                        throw new IllegalStateException(
+                                "Assigned message broker '"+groupConfig.getMessageBrokerId()+"' does not exist."
+                        );
+                    }
+
+                    ExecutableGroup executableGroup = executableGroupsMap.computeIfAbsent(
+                            groupId, k -> new ExecutableGroup(groupConfig)
+                    );
+
+                    MonitoredObject sensorMObj = sensor.id != null ? new MonitoredObject(sensor.id, period) : new AllMonitoredObjects(period);
+
+                    executableGroup.swapPeriod(sensorMObj.getPeriod());
+                    executableGroup.computeIfAbsent(mObject.id, MonitoredObject::new)
+                            .addNode(sensorMObj.getId(), sensorMObj);
+                }
+            }
+        }
+
+        for (Map.Entry<String, GroupConfig> groupEntry : groups.entrySet()) {
+            String groupId = groupEntry.getKey();
+            GroupConfig groupConfig = groupEntry.getValue();
+            if (!executableGroupsMap.containsKey(groupId)) {
+                MonitoredObject allSensors = new AllMonitoredObjects(groupConfig.getPeriod());
+                executableGroupsMap.computeIfAbsent(groupId, k -> new ExecutableGroup(groupConfig))
+                        .computeIfAbsent(groupId, k -> new AllMonitoredObjects(groupConfig.getPeriod()))
+                        .addNode(allSensors.getId(), allSensors);
+            }
+        }
+
+        Map<String, SuperGroup> executableSGroupsMap = new HashMap<>();
+        for (SuperGroupConfig sGConfig : superGroups.values()) {
+            if (sGConfig.getPeriod() == null) {
+                throw new IllegalStateException(
+                        "The monitored object '"+sGConfig.getId()+"' does not contain a valid 'period' value."
+                );
+            }
+
+            SuperGroup superGroup = new SuperGroup(sGConfig);
+            for (String groupId : sGConfig.getGroups()) {
+                ExecutableGroup group = executableGroupsMap.get(groupId);
+                if (group == null) {
+                    throw new IllegalStateException("Assigned group '"+groupId+"' does not exist.");
+                }
+                superGroup.addNode(groupId, group);
+            }
+            executableSGroupsMap.put(sGConfig.getId(), superGroup);
+        }
+
+        // TODO return
+        Collection<ExecutableGroup> execGroups = executableGroupsMap.values();
+        Collection<SuperGroup> execSGroups = executableSGroupsMap.values();
+        Collection<EmailServerConfig> emailServerConfigs = emailServers.values();
+        Collection<DataProviderConfig> dataProviderConfigs = dataProviders.values();
+        Collection<MessageBrokerConfig> messageBrokerConfigs = messageBrokers.values();
+
+        return new Configuration(generalConfig, execGroups,
+                emailServerConfigs, messageBrokerConfigs, dataProviderConfigs);
+    }
+
+    public static Configuration load(String fileName) throws IOException {
+
+        logger.info("Loading '{}' configuration file.", fileName);
+
+        if (!fileName.toLowerCase().endsWith(".yaml")) {
+            throw new IllegalArgumentException(fileName + "does not contain .yaml extension.");
+        }
+
+        Path filePath = Paths.get(fileName);
+        if (Files.notExists(filePath)) {
+            throw new FileNotFoundException(fileName + " does not exist");
+        }
+
+        Map<Object, Object> properties;
+
+        logger.debug("Opening the file '{}'.", fileName);
+        try (InputStream fileStream = Files.newInputStream(filePath)) {
+            logger.debug("Parsing the yaml file '{}'.", fileName);
+            properties = new Yaml().load(fileStream);
+            logger.debug("The configuration yaml file '{}' was parsed successfully.", fileName);
+        }
+
+        if (properties == null || properties.isEmpty()) {
+            throw new IOException(String.format(
+                    "The configuration yaml file %s is empty or was not loaded successfully. ", fileName
+            ));
+        }
+
+        try {
+            return make(
+                    parseGeneralConfig(createPropertyConfig(properties, "general")),
+                    parseMonitoredObjects(createPropertyConfig(properties, "monitoredObjects")),
+                    parseEmailServers(createPropertyConfig(properties, "emailServers")),
+                    parseMessageBrokers(createPropertyConfig(properties, "messageBrokers")),
+                    parseDataProviders(createPropertyConfig(properties, "dataProviders")),
+                    parseGroups(createPropertyConfig(properties, "groups")),
+                    parseSuperGroups(createPropertyConfig(properties, "superGroups"))
+            );
+        } catch (IOException e) {
+            throw new IOException(String.format(
+                    "Configuration file '%s' contains an error at '%s' attribute.", fileName, e.getMessage()
+            ));
+        }
+    }
+
+    private static GeneralConfig parseGeneralConfig(PropertyConfig config) {
+        return new GeneralConfig(config);
+    }
+
+    private static Map<String, EmailServerConfig> parseEmailServers(PropertyConfig config) {
+        Set<String> serverIds = config.getAttributes();
+        Map<String, EmailServerConfig> emailServers = new HashMap<>();
+        for (String id : serverIds) {
+            PropertyConfig serverConfig = config.getPropertyConfig(id);
+            emailServers.put(id, new EmailServerConfig(id,
+               serverConfig.getStringProperty("smtpHost"),
+               serverConfig.getIntegerProperty("smtpPort"),
+               serverConfig.getStringProperty("authUsername"),
+               serverConfig.getStringProperty("authPassword")
+            ));
+        }
+        return emailServers;
+    }
+
+    private static Map<String, MessageBrokerConfig> parseMessageBrokers(PropertyConfig config) {
+        Set<String> brokerIds = config.getAttributes();
+        Map<String, MessageBrokerConfig> messageBrokers = new HashMap<>(brokerIds.size());
+        for (String id : brokerIds) {
+            PropertyConfig brokerConfig = config.getPropertyConfig(id);
+            MessageBrokerType type = MessageBrokerType.of(brokerConfig.getStringProperty("type"));
+            messageBrokers.put(id, type.createConfig(id, brokerConfig.getPropertyConfig("config"))
+            );
+        }
+        return messageBrokers;
+    }
+
+    private static Map<String, DataProviderConfig> parseDataProviders(PropertyConfig config) {
+        Set<String> providerIds = config.getAttributes();
+        Map<String, DataProviderConfig> dataProviders = new HashMap<>(providerIds.size());
+        for (String id : providerIds) {
+            PropertyConfig providerConfig = config.getPropertyConfig(id);
+            DataProviderType type = DataProviderType.of(providerConfig.getStringProperty("type"));
+            dataProviders.put(id, type.createConfig(id, providerConfig.getPropertyConfig("config")));
+        }
+        return dataProviders;
+    }
+
+    private static Map<String, GroupConfig> parseGroups(PropertyConfig config) {
+        Set<String> groupIds = config.getAttributes();
+        Map<String, GroupConfig> groups = new HashMap<>(groupIds.size());
+        for (String id : groupIds) {
+            PropertyConfig groupConfig = config.getPropertyConfig(id);
+            groups.put(id, new GroupConfig(id,
+                    groupConfig.getStringProperty("name"),
+                    groupConfig.getStringProperty("dataProvider"),
+                    groupConfig.getStringProperty("messageBroker"),
+                    ResultType.of(groupConfig.getStringProperty("resultType")),
+                    groupConfig.getOptionalProperty("period", Integer.class).orElse(null)
+            ));
+        }
+        return groups;
+    }
+
+    private static Map<String, SuperGroupConfig> parseSuperGroups(PropertyConfig config) {
+        Set<String> groupIds = config.getAttributes();
+        Map<String, SuperGroupConfig> groups = new HashMap<>(groupIds.size());
+        for (String id : groupIds) {
+            PropertyConfig groupConfig = config.getPropertyConfig(id);
+
+            List<String> subGroupList = groupConfig.getArrayPropertyOf("groups", String.class);
+            Set<String> subGroupNames = new HashSet<>(subGroupList);
+            groups.put(id, new SuperGroupConfig(id,
+                    groupConfig.getStringProperty("name"),
+                    groupConfig.getStringProperty("messageBroker"),
+                    ResultType.of(groupConfig.getStringProperty("resultType")),
+                    groupConfig.getOptionalProperty("period", Integer.class).orElse(null),
+                    subGroupNames
+            ));
+        }
+        return groups;
+    }
+
+    private static List<TempUnitConfig> parseMonitoredObjects(PropertyConfig config) {
+        Set<String> unitIds = config.getAttributes();
+        List<TempUnitConfig> unitConfigs = new ArrayList<>(unitIds.size());
+        for (String unitId : unitIds) {
+            PropertyConfig unitConfig = config.getPropertyConfig(unitId);
+            Integer unitPeriod = unitConfig.getOptionalProperty("period", Integer.class).orElse(null);
+            List<String> unitGroupList = unitConfig.containsProperty("groups") ? unitConfig.getArrayPropertyOf("groups", String.class) : null;
+            Set<String> unitGroups = unitGroupList != null ? new HashSet<>(unitGroupList) : new HashSet<>();
+            List<TempSensorConfig> sensorConfigList = new ArrayList<>();
+            if (unitConfig.containsProperty("sensors")) {
+                Object sensorsObj = unitConfig.getProperty("sensors");
+                if (sensorsObj instanceof List) {
+                    List<Integer> sensors = unitConfig.getArrayPropertyOf("sensors", Integer.class);
+                    for (Integer sensorId : sensors) {
+                        sensorConfigList.add(new TempSensorConfig(Integer.toString(sensorId), unitPeriod, new HashSet<>()));
+                    }
+                } else if (sensorsObj instanceof Map) {
+                    PropertyConfig sensorsConfig = unitConfig.getPropertyConfig("sensors");
+                    Set<String> sensorIds = sensorsConfig.getAttributes();
+                    for (String sensorId : sensorIds) {
+                        PropertyConfig sensorConfig = sensorsConfig.getPropertyConfig(sensorId);
+                        Integer sensorPeriod = sensorConfig.getOptionalProperty("period", Integer.class).orElse(unitPeriod);
+                        List<String> sensorGroupList = sensorConfig.containsProperty("groups") ? sensorConfig.getArrayPropertyOf("groups", String.class) : emptyList();
+                        sensorConfigList.add(new TempSensorConfig(sensorId, sensorPeriod, new HashSet<>(sensorGroupList)));
+                    }
+                }
+            }
+            unitConfigs.add(new TempUnitConfig(unitId, unitPeriod, sensorConfigList, unitGroups));
+        }
+        return unitConfigs;
+    }
+
+    private static PropertyConfig createPropertyConfig(Map<Object, Object> properties, String propertyName) throws IOException {
+        Object generalConfig = properties.get(propertyName);
+        if (generalConfig == null) {
+            return new PropertyConfig(propertyName);
+        }
+        if (!(generalConfig instanceof Map)) {
+            throw new IOException(propertyName);
+        }
+
+        Map<?, ?> generalConfigMap = (Map<?, ?>) generalConfig;
+        PropertyConfig propertyConfig = new PropertyConfig(propertyName);
+
+        for (Map.Entry<?, ?> entry : generalConfigMap.entrySet()) {
+            String keyName = entry.getKey().toString();
+            propertyConfig.setProperty(keyName, entry.getValue());
+        }
+        return propertyConfig;
+    }
+
+    public Collection<MessageBrokerConfig> getMessageBrokerConfigs() {
+        return messageBrokerConfigs;
+    }
+
+    public Collection<EmailServerConfig> getEmailServerConfigs() {
+        return emailServerConfigs;
+    }
+
+    public Collection<DataProviderConfig> getDataProviderConfigs() {
+        return dataProviderConfigs;
+    }
+
+    public Collection<ExecutableGroup> getExecutableGroups() {
+        return executableGroups;
+    }
+
+    public GeneralConfig getGeneralConfig() {
+        return generalConfig;
+    }
+
+    private static class TempUnitConfig {
+        private final String id;
+        private final Integer period;
+        private final List<TempSensorConfig> sensors;
+        private final Set<String> groups;
+
+        private TempUnitConfig(String id, Integer period, List<TempSensorConfig> sensors, Set<String> groups) {
+            this.id = id;
+            this.period = period;
+            this.sensors = sensors;
+            this.groups = groups;
+        }
+    }
+
+    private static class TempSensorConfig {
+        private final String id;
+        private final Integer period;
+        private final Set<String> groups;
+
+        private TempSensorConfig(String id, Integer period, Set<String> groups) {
+            this.id = id;
+            this.period = period;
+            this.groups = groups;
+        }
+    }
+
+    private static class TempAllSensorsConfig extends TempSensorConfig {
+        private TempAllSensorsConfig(Integer period) {
+            super(null, period, new HashSet<>());
+        }
+    }
+}

+ 16 - 0
src/main/java/cz/senslog/watchdog/config/GeneralConfig.java

@@ -0,0 +1,16 @@
+package cz.senslog.watchdog.config;
+
+import java.time.LocalTime;
+
+public class GeneralConfig {
+
+    private final LocalTime firstStartAt;
+
+    public GeneralConfig(PropertyConfig config) {
+        this.firstStartAt = config.getLocalTimeProperty("firstStartAt");
+    }
+
+    public LocalTime getFirstStartAt() {
+        return firstStartAt;
+    }
+}

+ 242 - 226
src/main/java/cz/senslog/watchdog/config/PropertyConfig.java

@@ -1,226 +1,242 @@
-// Copyright (c) 2020 UWB & LESP.
-// The UWB & LESP license this file to you under the MIT license.
-
-package cz.senslog.watchdog.config;
-
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
-import java.util.*;
-
-import static java.lang.String.format;
-import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
-import static java.util.Optional.*;
-
-/**
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-public class PropertyConfig {
-
-    /** Path delimiter separates nodes. */
-    private static final String PATH_DELIMITER = ".";
-
-    /** Identifier of path. */
-    private final String id;
-
-    /** Map of properties. */
-    private final Map<String, Object> properties;
-
-    /**
-     * Constructor sets new identifier of node.
-     * @param id - identifier of node.
-     */
-    protected PropertyConfig(String id) {
-        this.id = id;
-        this.properties = new HashMap<>();
-    }
-
-    /**
-     * Adds new property to properties.
-     * @param name - name of new property.
-     */
-    public void setProperty(String name, Object value) {
-        properties.put(name, value);
-    }
-
-    /**
-     * Returns value. It could be anything.
-     * @param name - name of property.
-     * @return object of value.
-     */
-    public Object getProperty(String name) {
-        if (properties.containsKey(name)) {
-            return properties.get(name);
-        }
-
-        throw new IllegalArgumentException(format(
-                "Property '%s' does not exist.", getNewPropertyId(name))
-        );
-    }
-
-    /**
-     * Checks if property key is presents in properties.
-     * @param name - name of property
-     * @return boolean
-     */
-    public boolean containsProperty(String name) {
-        return properties.containsKey(name);
-    }
-
-    /**
-     * Returns optional value. It could be anything.
-     * @param name - name of property.
-     * @return optional object
-     */
-    @SuppressWarnings("unchecked")
-    public <T> Optional<T> getOptionalProperty(String name, Class<T> aClass) {
-        Object value = properties.get(name);
-        if (value == null || value.getClass() != aClass) {
-            return empty();
-        }
-
-        return of((T)value);
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> List<T> getArrayPropertyOf(String name, Class<T> aClass) {
-        Object value = getProperty(name);
-        List<?> list = value instanceof List ? (List<?>)value : null;
-        if (list == null) {
-            throw new IllegalArgumentException("Cast error " + value.getClass() + " to " + List.class);
-        }
-        List<T> result = new ArrayList<>(list.size());
-        for (Object v : list) {
-            if (v.getClass() == aClass) {
-                result.add((T)v);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Returns property as a String.
-     * @param name - name of property.
-     * @return string value.
-     */
-    public String getStringProperty(String name) {
-        Object value = getProperty(name);
-        if (value instanceof String) {
-            return (String)value;
-        }
-        throw new ClassCastException(format(
-                "Value '%s' can not be cast to String", value
-        ));
-    }
-
-    /**
-     * Returns property as an Integer.
-     * @param name - name of property.
-     * @return integer value.
-     */
-    public Integer getIntegerProperty(String name) {
-        Object value = getProperty(name);
-        if (value instanceof Integer) {
-            return (Integer)value;
-        }
-        throw new ClassCastException(format(
-                "Value '%s' can not be cast to Integer", value
-        ));
-    }
-
-    /**
-     * Returns property as an Integer. If the property is null, will return default value.
-     * @param name - name of property.
-     * @param defaultValue - default value if the property is null
-     * @return integer value
-     */
-    public Integer getIntegerProperty(String name, int defaultValue) {
-        Object value = getProperty(name);
-        if (value instanceof Integer) {
-            return (Integer)value;
-        }
-
-        return defaultValue;
-    }
-
-    /**
-     * Returns property as a LocalDateTime.
-     * @param name - name of property.
-     * @return localDateTime value.
-     */
-    public LocalDateTime getLocalDateTimeProperty(String name) {
-        Object object = getProperty(name);
-
-        if (object instanceof LocalDateTime) {
-            return (LocalDateTime) object;
-        } else if (object instanceof Date) {
-            Date date = (Date) object;
-            return date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalDateTime();
-        } else if (object instanceof String) {
-            return LocalDateTime.parse((String)object, DateTimeFormatter.ISO_DATE_TIME);
-        } else {
-            throw new ClassCastException(format(
-                    "Property '%s' can not be cast to %s", getNewPropertyId(name), LocalDateTime.class)
-            );
-        }
-    }
-
-    public ZonedDateTime getZonedDateTimeProperty(String name) {
-        Object object = getProperty(name);
-
-        if (object instanceof ZonedDateTime) {
-            return (ZonedDateTime)object;
-        } else if (object instanceof String) {
-            final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
-                    .append(ISO_LOCAL_DATE_TIME)
-                    .optionalStart()
-                    .appendLiteral('[')
-                    .parseCaseSensitive()
-                    .appendZoneRegionId()
-                    .appendLiteral(']')
-                    .toFormatter();
-            return ZonedDateTime.parse((String)object, formatter);
-        } else {
-            throw new ClassCastException(format(
-                    "Property '%s' can not be cast to %s", getNewPropertyId(name), ZonedDateTime.class)
-            );
-        }
-    }
-
-    /**
-     * Returns new node of configuration.
-     * @param name - name of property.
-     * @return node of configuration.
-     */
-    public PropertyConfig getPropertyConfig(String name) {
-        Object property = getProperty(name);
-        PropertyConfig config = new PropertyConfig(getNewPropertyId(name));
-
-        if (property instanceof Map) {
-            Map<?, ?> properties = (Map<?, ?>) property;
-            for (Map.Entry<?, ?> propertyEntry : properties.entrySet()) {
-                Object propertyName = propertyEntry.getKey();
-                config.setProperty(propertyName.toString(), propertyEntry.getValue());
-            }
-        }
-
-        return config;
-    }
-
-    public Set<String> getAttributes() {
-        return properties.keySet();
-    }
-
-    private String getNewPropertyId(String name) {
-        return id + PATH_DELIMITER + name;
-    }
-
-    public String getId() {
-        return id;
-    }
-}
+// Copyright (c) 2020 UWB & LESP.
+// The UWB & LESP license this file to you under the MIT license.
+
+package cz.senslog.watchdog.config;
+
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.*;
+
+import static java.lang.String.format;
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+import static java.util.Optional.*;
+
+/**
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+public class PropertyConfig {
+
+    /** Path delimiter separates nodes. */
+    private static final String PATH_DELIMITER = ".";
+
+    /** Identifier of path. */
+    private final String id;
+
+    /** Map of properties. */
+    private final Map<String, Object> properties;
+
+    /**
+     * Constructor sets new identifier of node.
+     * @param id - identifier of node.
+     */
+    protected PropertyConfig(String id) {
+        this.id = id;
+        this.properties = new HashMap<>();
+    }
+
+    /**
+     * Adds new property to properties.
+     * @param name - name of new property.
+     */
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /**
+     * Returns value. It could be anything.
+     * @param name - name of property.
+     * @return object of value.
+     */
+    public Object getProperty(String name) {
+        if (properties.containsKey(name)) {
+            return properties.get(name);
+        }
+
+        throw new IllegalArgumentException(format(
+                "Property '%s' does not exist.", getNewPropertyId(name))
+        );
+    }
+
+    /**
+     * Checks if property key is presents in properties.
+     * @param name - name of property
+     * @return boolean
+     */
+    public boolean containsProperty(String name) {
+        return properties.containsKey(name);
+    }
+
+    /**
+     * Returns optional value. It could be anything.
+     * @param name - name of property.
+     * @return optional object
+     */
+    @SuppressWarnings("unchecked")
+    public <T> Optional<T> getOptionalProperty(String name, Class<T> aClass) {
+        Object value = properties.get(name);
+        if (value == null || value.getClass() != aClass) {
+            return empty();
+        }
+
+        return of((T)value);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> List<T> getArrayPropertyOf(String name, Class<T> aClass) {
+        Object value = getProperty(name);
+        List<?> list = value instanceof List ? (List<?>)value : null;
+        if (list == null) {
+            throw new IllegalArgumentException("Cast error " + value.getClass() + " to " + List.class);
+        }
+        List<T> result = new ArrayList<>(list.size());
+        for (Object v : list) {
+            if (v.getClass() == aClass) {
+                result.add((T)v);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns property as a String.
+     * @param name - name of property.
+     * @return string value.
+     */
+    public String getStringProperty(String name) {
+        Object value = getProperty(name);
+        if (value instanceof String) {
+            return (String)value;
+        }
+        throw new ClassCastException(format(
+                "Value '%s' can not be cast to String", value
+        ));
+    }
+
+    /**
+     * Returns property as an Integer.
+     * @param name - name of property.
+     * @return integer value.
+     */
+    public Integer getIntegerProperty(String name) {
+        Object value = getProperty(name);
+        if (value instanceof Integer) {
+            return (Integer)value;
+        }
+        throw new ClassCastException(format(
+                "Value '%s' can not be cast to Integer", value
+        ));
+    }
+
+    /**
+     * Returns property as an Integer. If the property is null, will return default value.
+     * @param name - name of property.
+     * @param defaultValue - default value if the property is null
+     * @return integer value
+     */
+    public Integer getIntegerProperty(String name, int defaultValue) {
+        Object value = getProperty(name);
+        if (value instanceof Integer) {
+            return (Integer)value;
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Returns property as a LocalDateTime.
+     * @param name - name of property.
+     * @return localDateTime value.
+     */
+    public LocalDateTime getLocalDateTimeProperty(String name) {
+        Object object = getProperty(name);
+
+        if (object instanceof LocalDateTime) {
+            return (LocalDateTime) object;
+        } else if (object instanceof Date) {
+            Date date = (Date) object;
+            return date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalDateTime();
+        } else if (object instanceof String) {
+            return LocalDateTime.parse((String)object, DateTimeFormatter.ISO_DATE_TIME);
+        } else {
+            throw new ClassCastException(format(
+                    "Property '%s' can not be cast to %s", getNewPropertyId(name), LocalDateTime.class)
+            );
+        }
+    }
+
+    public LocalTime getLocalTimeProperty(String name) {
+        Object object = getProperty(name);
+
+        if (object instanceof LocalTime) {
+            return (LocalTime) object;
+        } else if (object instanceof String) {
+            return LocalTime.parse((String) object);
+        } else {
+            throw new ClassCastException(format(
+                    "Property '%s' can not be cast to %s", getNewPropertyId(name), LocalTime.class)
+            );
+        }
+    }
+
+    public ZonedDateTime getZonedDateTimeProperty(String name) {
+        Object object = getProperty(name);
+
+        if (object instanceof ZonedDateTime) {
+            return (ZonedDateTime)object;
+        } else if (object instanceof String) {
+            final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+                    .append(ISO_LOCAL_DATE_TIME)
+                    .optionalStart()
+                    .appendLiteral('[')
+                    .parseCaseSensitive()
+                    .appendZoneRegionId()
+                    .appendLiteral(']')
+                    .toFormatter();
+            return ZonedDateTime.parse((String)object, formatter);
+        } else {
+            throw new ClassCastException(format(
+                    "Property '%s' can not be cast to %s", getNewPropertyId(name), ZonedDateTime.class)
+            );
+        }
+    }
+
+    /**
+     * Returns new node of configuration.
+     * @param name - name of property.
+     * @return node of configuration.
+     */
+    public PropertyConfig getPropertyConfig(String name) {
+        Object property = getProperty(name);
+        PropertyConfig config = new PropertyConfig(getNewPropertyId(name));
+
+        if (property instanceof Map) {
+            Map<?, ?> properties = (Map<?, ?>) property;
+            for (Map.Entry<?, ?> propertyEntry : properties.entrySet()) {
+                Object propertyName = propertyEntry.getKey();
+                config.setProperty(propertyName.toString(), propertyEntry.getValue());
+            }
+        }
+
+        return config;
+    }
+
+    public Set<String> getAttributes() {
+        return properties.keySet();
+    }
+
+    private String getNewPropertyId(String name) {
+        return id + PATH_DELIMITER + name;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+}

+ 1 - 9
src/main/java/cz/senslog/watchdog/config/WSSensLogDataProviderConfig.java

@@ -2,7 +2,6 @@ package cz.senslog.watchdog.config;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Optional;
 
 public class WSSensLogDataProviderConfig extends DataProviderConfig {
 
@@ -28,23 +27,20 @@ public class WSSensLogDataProviderConfig extends DataProviderConfig {
     private final String baseUrl;
     private final URI baseUri;
     private final String groupName;
-    private final String userName;
     private final SensLogAuthConfig auth;
 
     public static WSSensLogDataProviderConfig create(String id, PropertyConfig config) {
         return new WSSensLogDataProviderConfig(id,
                 config.getStringProperty("baseUrl"),
                 config.getStringProperty("groupName"),
-                config.getStringProperty("userName"),
                 new SensLogAuthConfig(config.getPropertyConfig("auth"))
         );
     }
 
-    public WSSensLogDataProviderConfig(String id, String baseUrl, String groupName, String userName, SensLogAuthConfig auth) {
+    public WSSensLogDataProviderConfig(String id, String baseUrl, String groupName, SensLogAuthConfig auth) {
         super(id, DataProviderType.WEB_SERVICE);
         this.baseUrl = baseUrl;
         this.groupName = groupName;
-        this.userName = userName;
         this.auth = auth;
         try {
             this.baseUri = new URI(baseUrl);
@@ -65,10 +61,6 @@ public class WSSensLogDataProviderConfig extends DataProviderConfig {
         return groupName;
     }
 
-    public String getUserName() {
-        return userName;
-    }
-
     public SensLogAuthConfig getAuth() {
         return auth;
     }

+ 7 - 16
src/main/java/cz/senslog/watchdog/provider/ws/WSSensLogDataProvider.java

@@ -25,7 +25,6 @@ public class WSSensLogDataProvider implements DataProvider {
     private static final String ERROR_KEY = "__error";
 
     private static final String DEFAULT_NAME = "unknown";
-    private static final Tuple<String, Map<String, String>> DEFAULT_UNIT_INFO = Tuple.of(DEFAULT_NAME, emptyMap());
 
     private static final DateTimeFormatter TIMESTAMP_PATTERN = ofPattern("yyyy-MM-dd HH:mm:ssX");
 
@@ -39,7 +38,7 @@ public class WSSensLogDataProvider implements DataProvider {
         this.authCookie = Tuple.of(false, null);
     }
 
-    /* Map<unitId, Tuple<unitName, Map<sensorId, sensorName>>> */
+    /*
     private Map<String, Tuple<String, Map<String, String>>> loadUnitsInfo() {
         HttpCookie authCookie = getAuthCookie();
         if (!authCookie.isSecure()) {
@@ -99,7 +98,7 @@ public class WSSensLogDataProvider implements DataProvider {
 
         return unitsInfo;
     }
-
+*/
     private List<Map<String, Object>> loadLastObservations() {
         HttpCookie authCookie = getAuthCookie();
         if (!authCookie.isSecure()) {
@@ -108,10 +107,8 @@ public class WSSensLogDataProvider implements DataProvider {
         }
 
         HttpRequest request = HttpRequest.newBuilder().GET()
-                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/SensorService")
-                        .addParam("Operation", "GetLastObservations")
-                        .addParam("group", config.getGroupName())
-                        .addParam("user", config.getUserName())
+                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/rest/watchdog/group")
+                        .addParam("group_name", config.getGroupName())
                         .build())
                 .addCookie(authCookie)
                 .build();
@@ -175,13 +172,9 @@ public class WSSensLogDataProvider implements DataProvider {
     @Override
     public ProvidedData getLastData() {
 
-        Map<String, Tuple<String, Map<String, String>>> unitsInfo = loadUnitsInfo();
         List<Map<String, Object>> lastObservations = loadLastObservations();
 
         List<String> errorMessages = new ArrayList<>();
-        if (unitsInfo.containsKey(ERROR_KEY)) {
-            errorMessages.add(unitsInfo.get(ERROR_KEY).getItem1());
-        }
         boolean isServerAlive = true;
         if (!lastObservations.isEmpty() && lastObservations.get(0).containsKey(ERROR_KEY)) {
             errorMessages.add((String) lastObservations.get(0).get(ERROR_KEY));
@@ -194,16 +187,14 @@ public class WSSensLogDataProvider implements DataProvider {
         if (isServerAlive) {
             for (Map<String, Object> obMap : lastObservations) {
                 long unitId = ((Double) obMap.get("unitId")).longValue();
+                String unitName = (String) obMap.getOrDefault("unitName", DEFAULT_NAME);
                 long sensorId = ((Double) obMap.get("sensorId")).longValue();
-                OffsetDateTime timestamp = OffsetDateTime.parse(obMap.get("timeStamp").toString(), TIMESTAMP_PATTERN);
+                String sensorName = (String) obMap.getOrDefault("sensorName", DEFAULT_NAME);
+                OffsetDateTime timestamp = OffsetDateTime.parse((String)obMap.get("lastTimest"), TIMESTAMP_PATTERN);
 
                 String unitIdStr = String.format("%d", unitId);
                 String sensorIdStr = String.format("%d", sensorId);
 
-                Tuple<String, Map<String, String>> unitInfo = unitsInfo.getOrDefault(unitIdStr, DEFAULT_UNIT_INFO);
-                String unitName = unitInfo.getItem1();
-                String sensorName = unitInfo.getItem2().getOrDefault(sensorIdStr, DEFAULT_NAME);
-
                 providedData.computeIfAbsent(unitIdStr, k -> new ProvidedObject(k, unitName, null))
                         .addNode(sensorIdStr, new ProvidedObject(sensorIdStr, sensorName, timestamp));
             }