Bläddra i källkod

Added authorization for SensLog 15 instance

Lukas Cerny 4 år sedan
förälder
incheckning
c189cf8317

+ 13 - 13
Dockerfile

@@ -1,14 +1,14 @@
-FROM adoptopenjdk/openjdk11:jdk-11.0.10_9-debian AS builder
-ARG config_file
-COPY src /app/src
-COPY $config_file /app/config.yaml
-COPY gradle /app/gradle
-COPY build.gradle settings.gradle gradlew /app/
-WORKDIR /app/
-RUN ./gradlew assemble
-
-FROM adoptopenjdk/openjdk11:jre-11.0.10_9-alpine AS production
-WORKDIR /app
-COPY --from=builder /app/build/libs /app/bin
-COPY --from=builder /app/config.yaml /app/
+FROM adoptopenjdk/openjdk11:jdk-11.0.10_9-debian AS builder
+ARG config_file
+COPY src /app/src
+COPY $config_file /app/config.yaml
+COPY gradle /app/gradle
+COPY build.gradle settings.gradle gradlew /app/
+WORKDIR /app/
+RUN ./gradlew assemble
+
+FROM adoptopenjdk/openjdk11:jre-11.0.10_9-alpine AS production
+WORKDIR /app
+COPY --from=builder /app/build/libs /app/bin
+COPY --from=builder /app/config.yaml /app/
 CMD java -cp "bin/*" -DlogPath="/var/log/watchdog-app" cz.senslog.watchdog.app.Main -cf config.yaml

+ 90 - 84
config/foodie.yaml

@@ -1,85 +1,91 @@
-emailServers:
-  lspEmail:
-    smtpHost: "mail.lesprojekt.cz"
-    smtpPort: 465
-    authUsername: "watchdog@senslog.org"
-    authPassword: "5jspdD"
-
-messageBrokers:
-  emailToAll:
-    type: EMAIL
-    config:
-      server: lspEmail
-      senderEmail: "watchdog@senslog.org"
-      recipientEmail: "luc.cerny@gmail.com, kepka@ccss.cz"
-      subject: "[Watchdog] Report Foodie SensLog (CZ)"
-
-dataProviders:
-  wsOldFoodieKynsperk:
-    type: WEB_SERVICE
-    config:
-      baseUrl: "http://sensor.lesprojekt.cz/DBServiceST"
-      userName: "kynsperk"
-      groupName: "kynsperk"
-
-  wsOldFoodieRostenice:
-    type: WEB_SERVICE
-    config:
-      baseUrl: "http://sensor.lesprojekt.cz/DBServiceST"
-      userName: "mikrsoil"
-      groupName: "rostenice_pudni"
-
-groups:
-  kynsperk:
-    name: "Kynsperk"
-    dataProvider: wsOldFoodieKynsperk
-    messageBroker: emailToAll
-    resultType: FAIL
-    period: 86400
-
-  rostenice:
-    name: "Rostenice"
-    dataProvider: wsOldFoodieRostenice
-    messageBroker: emailToAll
-    resultType: FAIL
-    period: 86400
-
-
-monitoredObjects:
-  1305167562293765: # no data
-    period: 86400
-    groups: [kynsperk]
-    sensors: [ 340340092, 360200000, 410130092 ]
-
-  1305167562275270: # no data
-    period: 86400
-    groups: [kynsperk]
-    sensors: [ 340240003, 360200000, 410090003 ]
-
-  1305167562292824: # no data
-    period: 86400
-    groups: [kynsperk]
-
-  1305167549173046: # no data
-    period: 86400
-    groups: [ kynsperk ]
-
-  1305167549144045:
-    period: 86400
-    groups: [ kynsperk ]
-
-  1305167549158198:
-    period: 86400
-    groups: [ kynsperk ]
-
-  1305167549167050:
-    period: 86400
-    groups: [ kynsperk ]
-
-  1305167549167886:
-    period: 86400
-    groups: [ kynsperk ]
-
-  1305167549149707:
-    period: 86400
+emailServers:
+  lspEmail:
+    smtpHost: "mail.lesprojekt.cz"
+    smtpPort: 465
+    authUsername: "watchdog@senslog.org"
+    authPassword: "5jspdD"
+
+messageBrokers:
+  emailToAll:
+    type: EMAIL
+    config:
+      server: lspEmail
+      senderEmail: "watchdog@senslog.org"
+      recipientEmail: "luccerny@ntis.zcu.cz, kepka@ccss.cz"
+      subject: "[Watchdog] Report Foodie SensLog (CZ)"
+
+dataProviders:
+  wsOldFoodieKynsperk:
+    type: WEB_SERVICE
+    config:
+      baseUrl: "http://sensor.lesprojekt.cz/senslog15"
+      userName: "kynsperk"
+      groupName: "kynsperk"
+      auth:
+        username: "watchdog"
+        password: "HAFhaf"
+
+  wsOldFoodieRostenice:
+    type: WEB_SERVICE
+    config:
+      baseUrl: "http://sensor.lesprojekt.cz/senslog15"
+      userName: "mikrsoil"
+      groupName: "rostenice_pudni"
+      auth:
+        username: "watchdog"
+        password: "HAFhaf"
+
+groups:
+  kynsperk:
+    name: "Kynsperk"
+    dataProvider: wsOldFoodieKynsperk
+    messageBroker: emailToAll
+    resultType: FAIL
+    period: 86400
+
+  rostenice:
+    name: "Rostenice"
+    dataProvider: wsOldFoodieRostenice
+    messageBroker: emailToAll
+    resultType: FAIL
+    period: 86400
+
+
+monitoredObjects:
+  1305167562293765: # no data
+    period: 86400
+    groups: [kynsperk]
+    sensors: [ 340340092, 360200000, 410130092 ]
+
+  1305167562275270: # no data
+    period: 86400
+    groups: [kynsperk]
+    sensors: [ 340240003, 360200000, 410090003 ]
+
+  1305167562292824: # no data
+    period: 86400
+    groups: [kynsperk]
+
+  1305167549173046: # no data
+    period: 86400
+    groups: [ kynsperk ]
+
+  1305167549144045:
+    period: 86400
+    groups: [ kynsperk ]
+
+  1305167549158198:
+    period: 86400
+    groups: [ kynsperk ]
+
+  1305167549167050:
+    period: 86400
+    groups: [ kynsperk ]
+
+  1305167549167886:
+    period: 86400
+    groups: [ kynsperk ]
+
+  1305167549149707:
+    period: 86400
     groups: [ kynsperk ]

+ 75 - 35
src/main/java/cz/senslog/watchdog/config/WSSensLogDataProviderConfig.java

@@ -1,35 +1,75 @@
-package cz.senslog.watchdog.config;
-
-public class WSSensLogDataProviderConfig extends DataProviderConfig {
-
-    private final String baseUrl;
-    private final String groupName;
-    private final String userName;
-
-    public static WSSensLogDataProviderConfig create(String id, PropertyConfig config) {
-        return new WSSensLogDataProviderConfig(id,
-                config.getStringProperty("baseUrl"),
-                config.getStringProperty("groupName"),
-                config.getStringProperty("userName")
-        );
-    }
-
-    public WSSensLogDataProviderConfig(String id, String baseUrl, String groupName, String userName) {
-        super(id, DataProviderType.WEB_SERVICE);
-        this.baseUrl = baseUrl;
-        this.groupName = groupName;
-        this.userName = userName;
-    }
-
-    public String getBaseUrl() {
-        return baseUrl;
-    }
-
-    public String getGroupName() {
-        return groupName;
-    }
-
-    public String getUserName() {
-        return userName;
-    }
-}
+package cz.senslog.watchdog.config;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+
+public class WSSensLogDataProviderConfig extends DataProviderConfig {
+
+    public static class SensLogAuthConfig {
+
+        private final String username;
+        private final String password;
+
+        private SensLogAuthConfig(PropertyConfig config) {
+            this.username = config.getStringProperty("username");
+            this.password = config.getStringProperty("password");
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+    }
+
+    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) {
+        super(id, DataProviderType.WEB_SERVICE);
+        this.baseUrl = baseUrl;
+        this.groupName = groupName;
+        this.userName = userName;
+        this.auth = auth;
+        try {
+            this.baseUri = new URI(baseUrl);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("The config is in invalid format. " + e.getMessage());
+        }
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public URI getBaseURI() {
+        return baseUri;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public SensLogAuthConfig getAuth() {
+        return auth;
+    }
+}

+ 213 - 160
src/main/java/cz/senslog/watchdog/provider/ws/WSSensLogDataProvider.java

@@ -1,160 +1,213 @@
-package cz.senslog.watchdog.provider.ws;
-
-import com.google.gson.reflect.TypeToken;
-import cz.senslog.watchdog.config.WSSensLogDataProviderConfig;
-import cz.senslog.watchdog.provider.*;
-import cz.senslog.watchdog.util.Tuple;
-import cz.senslog.watchdog.util.http.HttpClient;
-import cz.senslog.watchdog.util.http.HttpRequest;
-import cz.senslog.watchdog.util.http.HttpResponse;
-import cz.senslog.watchdog.util.http.URLBuilder;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.lang.reflect.Type;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-
-import static cz.senslog.watchdog.config.DataProviderType.WEB_SERVICE;
-import static cz.senslog.watchdog.util.json.BasicJson.jsonToObject;
-import static java.time.format.DateTimeFormatter.ofPattern;
-import static java.util.Collections.*;
-
-public class WSSensLogDataProvider implements DataProvider {
-
-    private static final Logger logger = LogManager.getLogger(WSSensLogDataProvider.class);
-
-    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");
-
-    private final HttpClient httpClient = HttpClient.newHttpClient();
-    private final WSSensLogDataProviderConfig config;
-
-    public WSSensLogDataProvider(WSSensLogDataProviderConfig config) {
-        this.config = config;
-    }
-
-    /* Map<unitId, Tuple<unitName, Map<sensorId, sensorName>>> */
-    private Map<String, Tuple<String, Map<String, String>>> loadUnitsInfo() {
-        HttpRequest request = HttpRequest.newBuilder().GET()
-                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/DataService")
-                        .addParam("Operation", "GetUnits")
-                        .addParam("group", config.getGroupName())
-                        .addParam("user", config.getUserName())
-                        .build()
-                ).build();
-        logger.info("Getting new data from the server: {}.", request.getUrl());
-
-        HttpResponse response = httpClient.send(request);
-        logger.info("Received data with the status '{}' from the server {}.", response.getStatus(), request.getUrl());
-
-        if (response.isError()) {
-            logger.error("code: {}, message: {}", response.getStatus(), response.getBody());
-            Map<String, Tuple<String, Map<String, String>>> errorMap = new HashMap<>();
-            errorMap.put(ERROR_KEY, Tuple.of(response.getBody(), emptyMap()));
-            return errorMap;
-        }
-
-        Map<String, Tuple<String, Map<String, String>>> unitsInfo = new HashMap<>();
-
-        final Type unitInfoType = new TypeToken<Collection<Map<String, Object>>>() {}.getType();
-        List<Map<String, Object>> unitInfoList = jsonToObject(response.getBody(), unitInfoType);
-        for (Map<String, Object> unitInfo : unitInfoList) {
-            Object unitObj = unitInfo.get("unit");
-            if (unitObj instanceof Map) {
-                Map<?, ?> unitMap = (Map<?, ?>)unitObj;
-                String unitName = unitMap.get("description").toString();
-                String unitId = String.format("%d", ((Double)unitMap.get("unitId")).longValue());
-
-                Map<String, String> resultSensorInfo = new HashMap<>();
-                Object sensorsObj = unitInfo.get("sensors");
-                if (sensorsObj instanceof List) {
-                    List<?> sensorsList = (List<?>)sensorsObj;
-                    for (Object sensorInfoObj : sensorsList) {
-                        if (sensorInfoObj instanceof Map) {
-                            Map<?, ?> sensorMap = (Map<?, ?>)sensorInfoObj;
-                            String sensorName = sensorMap.get("sensorName").toString();
-                            String sensorId = String.format("%d", ((Double)sensorMap.get("sensorId")).longValue());
-
-                            resultSensorInfo.put(sensorId, sensorName);
-                        }
-                    }
-                }
-
-                unitsInfo.put(unitId, Tuple.of(unitName, resultSensorInfo));
-            }
-        }
-
-        return unitsInfo;
-    }
-
-    private List<Map<String, Object>> loadLastObservations() {
-        HttpRequest request = HttpRequest.newBuilder().GET()
-                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/SensorService")
-                        .addParam("Operation", "GetLastObservations")
-                        .addParam("group", config.getGroupName())
-                        .addParam("user", config.getUserName())
-                        .build()
-                ).build();
-        logger.info("Getting new data from the server: {}.", request.getUrl());
-
-        HttpResponse response = httpClient.send(request);
-        logger.info("Received data with the status '{}' from the server {}.", response.getStatus(), request.getUrl());
-
-        if (response.isError()) {
-            logger.error("code: {}, message: {}", response.getStatus(), response.getBody());
-            Map<String, Object> errorMap = new HashMap<>();
-            errorMap.put(ERROR_KEY, response.getBody());
-            return singletonList(errorMap);
-        }
-
-        final Type lastObsType = new TypeToken<Collection<Map<String, Object>>>() {}.getType();
-        return jsonToObject(response.getBody(), lastObsType);
-    }
-
-    @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));
-            isServerAlive = false;
-        }
-
-        String source = String.format("%s: %s", config.getId(), config.getBaseUrl());
-        ProvidedData providedData = new ProvidedData(new ProviderInfo(source, WEB_SERVICE, isServerAlive), errorMessages);
-
-        if (isServerAlive) {
-            for (Map<String, Object> obMap : lastObservations) {
-                long unitId = ((Double) obMap.get("unitId")).longValue();
-                long sensorId = ((Double) obMap.get("sensorId")).longValue();
-                OffsetDateTime timestamp = OffsetDateTime.parse(obMap.get("timeStamp").toString(), 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));
-            }
-        }
-
-        return providedData;
-    }
-
-}
+package cz.senslog.watchdog.provider.ws;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.watchdog.config.WSSensLogDataProviderConfig;
+import cz.senslog.watchdog.provider.*;
+import cz.senslog.watchdog.util.Tuple;
+import cz.senslog.watchdog.util.http.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+import static cz.senslog.watchdog.config.DataProviderType.WEB_SERVICE;
+import static cz.senslog.watchdog.util.json.BasicJson.jsonToObject;
+import static java.time.format.DateTimeFormatter.ofPattern;
+import static java.util.Collections.*;
+
+public class WSSensLogDataProvider implements DataProvider {
+
+    private static final Logger logger = LogManager.getLogger(WSSensLogDataProvider.class);
+
+    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");
+
+    private final HttpClient httpClient = HttpClient.newHttpClient();
+    private final WSSensLogDataProviderConfig config;
+
+    private Tuple<Boolean, HttpCookie> authCookie;
+
+    public WSSensLogDataProvider(WSSensLogDataProviderConfig config) {
+        this.config = config;
+        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()) {
+            logger.warn("Auth cookie is not valid to be used.");
+            return emptyMap();
+        }
+
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/DataService")
+                        .addParam("Operation", "GetUnits")
+                        .addParam("group", config.getGroupName())
+                        .addParam("user", config.getUserName())
+                        .build())
+                .addCookie(authCookie)
+                .build();
+        logger.info("Getting new data from the server: {}.", request.getUrl());
+
+        HttpResponse response = httpClient.send(request);
+        logger.info("Received data with the status '{}' from the server {}.", response.getStatus(), request.getUrl());
+
+        if (response.isError()) {
+            logger.error("code: {}, message: {}", response.getStatus(), response.getBody());
+            Map<String, Tuple<String, Map<String, String>>> errorMap = new HashMap<>();
+            errorMap.put(ERROR_KEY, Tuple.of(response.getBody(), emptyMap()));
+            return errorMap;
+        }
+
+        Map<String, Tuple<String, Map<String, String>>> unitsInfo = new HashMap<>();
+
+        final Type unitInfoType = new TypeToken<Collection<Map<String, Object>>>() {}.getType();
+        List<Map<String, Object>> unitInfoList = jsonToObject(response.getBody(), unitInfoType);
+        for (Map<String, Object> unitInfo : unitInfoList) {
+            Object unitObj = unitInfo.get("unit");
+            if (unitObj instanceof Map) {
+                Map<?, ?> unitMap = (Map<?, ?>)unitObj;
+                String unitName = unitMap.get("description").toString();
+                String unitId = String.format("%d", ((Double)unitMap.get("unitId")).longValue());
+
+                Map<String, String> resultSensorInfo = new HashMap<>();
+                Object sensorsObj = unitInfo.get("sensors");
+                if (sensorsObj instanceof List) {
+                    List<?> sensorsList = (List<?>)sensorsObj;
+                    for (Object sensorInfoObj : sensorsList) {
+                        if (sensorInfoObj instanceof Map) {
+                            Map<?, ?> sensorMap = (Map<?, ?>)sensorInfoObj;
+                            String sensorName = sensorMap.get("sensorName").toString();
+                            String sensorId = String.format("%d", ((Double)sensorMap.get("sensorId")).longValue());
+
+                            resultSensorInfo.put(sensorId, sensorName);
+                        }
+                    }
+                }
+
+                unitsInfo.put(unitId, Tuple.of(unitName, resultSensorInfo));
+            }
+        }
+
+        return unitsInfo;
+    }
+
+    private List<Map<String, Object>> loadLastObservations() {
+        HttpCookie authCookie = getAuthCookie();
+        if (!authCookie.isSecure()) {
+            logger.warn("Auth cookie is not valid to be used.");
+            return emptyList();
+        }
+
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/SensorService")
+                        .addParam("Operation", "GetLastObservations")
+                        .addParam("group", config.getGroupName())
+                        .addParam("user", config.getUserName())
+                        .build())
+                .addCookie(authCookie)
+                .build();
+        logger.info("Getting new data from the server: {}.", request.getUrl());
+
+        HttpResponse response = httpClient.send(request);
+        logger.info("Received data with the status '{}' from the server {}.", response.getStatus(), request.getUrl());
+
+        if (response.isError()) {
+            logger.error("code: {}, message: {}", response.getStatus(), response.getBody());
+            Map<String, Object> errorMap = new HashMap<>();
+            errorMap.put(ERROR_KEY, response.getBody());
+            return singletonList(errorMap);
+        }
+
+        final Type lastObsType = new TypeToken<Collection<Map<String, Object>>>() {}.getType();
+        return jsonToObject(response.getBody(), lastObsType);
+    }
+
+    private HttpCookie getAuthCookie() {
+        if (authCookie.getItem1()) {
+            return authCookie.getItem2();
+        }
+
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(config.getBaseUrl(), "/ControllerServlet")
+                        .addParam("username", config.getAuth().getUsername())
+                        .addParam("password", config.getAuth().getPassword())
+                        .build()
+                ).build();
+        logger.info("Getting new data from the server: {}.", request.getUrl());
+
+        HttpResponse response = httpClient.send(request);
+        logger.info("Received data with the status '{}' from the server {}.", response.getStatus(), request.getUrl());
+
+        if (response.isError()) {
+            logger.warn("Authorization failed. Error code {} with the reason '{}'.", response.getStatus(), response.getBody());
+            HttpCookie cookie = HttpCookie.empty();
+            authCookie = Tuple.of(false, cookie);
+            return cookie;
+        }
+
+        final Type lastObsType = new TypeToken<Map<String, Object>>() {}.getType();
+        Map<String, Object> jsonResponse = jsonToObject(response.getBody(), lastObsType);
+
+        if (!jsonResponse.containsKey("sessionid")) {
+            logger.error("Authorization failed. JSON does not contain session id. {}", response.getBody());
+            HttpCookie cookie = HttpCookie.empty();
+            authCookie = Tuple.of(false, cookie);
+            return cookie;
+        }
+
+        String sessionId = (String) jsonResponse.get("sessionid");
+        String domain = config.getBaseURI().getHost();
+        String path = config.getBaseURI().getPath();
+        HttpCookie cookie = new HttpCookie("JSESSIONID", sessionId, domain, path);
+        authCookie = Tuple.of(true, cookie);
+        return cookie;
+    }
+
+    @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));
+            isServerAlive = false;
+        }
+
+        String source = String.format("%s: %s", config.getId(), config.getBaseUrl());
+        ProvidedData providedData = new ProvidedData(new ProviderInfo(source, WEB_SERVICE, isServerAlive), errorMessages);
+
+        if (isServerAlive) {
+            for (Map<String, Object> obMap : lastObservations) {
+                long unitId = ((Double) obMap.get("unitId")).longValue();
+                long sensorId = ((Double) obMap.get("sensorId")).longValue();
+                OffsetDateTime timestamp = OffsetDateTime.parse(obMap.get("timeStamp").toString(), 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));
+            }
+        }
+        return providedData;
+    }
+}

+ 202 - 188
src/main/java/cz/senslog/watchdog/util/http/HttpClient.java

@@ -1,188 +1,202 @@
-package cz.senslog.watchdog.util.http;
-
-import cz.senslog.watchdog.util.StringUtils;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpMessage;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.TrustStrategy;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.ssl.SSLContextBuilder;
-import org.apache.http.util.EntityUtils;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.apache.http.HttpHeaders.*;
-
-/**
- * The class {@code HttpClient} represents a wrapper for {@link org.apache.http.client.HttpClient}.
- * Provides functionality of sending GET and POST request. Otherwise is returned response with {@see #BAD_REQUEST}.
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-public class HttpClient {
-
-    /** Instance of http client. */
-    private final org.apache.http.client.HttpClient client;
-
-    /**
-     * Factory method to create a new instance of client.
-     * @return new instance of {@code HttpClient}.
-     */
-    public static HttpClient newHttpClient() {
-        return new HttpClient(HttpClientBuilder.create());
-    }
-
-    /**
-     * Private constructors sets http client.
-     */
-    private HttpClient(HttpClientBuilder httpClientBuilder) {
-        this.client = httpClientBuilder.build();
-    }
-
-    /**
-     * Sends http request.
-     * @param request - virtual request.
-     * @return virtual response.
-     */
-    public HttpResponse send(HttpRequest request) {
-        try {
-            switch (request.getMethod()) {
-                case GET:  return sendGet(request);
-                case POST: return sendPost(request);
-                default: return HttpResponse.newBuilder()
-                            .body("Request does not contain method definition.")
-                            .status(HttpCode.METHOD_NOT_ALLOWED).build();
-            }
-        } catch (URISyntaxException e) {
-            return HttpResponse.newBuilder()
-                    .body(e.getMessage()).status(HttpCode.BAD_REQUEST)
-                    .build();
-        } catch (IOException e) {
-            return  HttpResponse.newBuilder()
-                    .body(e.getMessage()).status(HttpCode.SERVER_ERROR)
-                    .build();
-        }
-    }
-
-    /**
-     * Sends GET request.
-     * @param request - virtual request.
-     * @return virtual response of the request.
-     * @throws URISyntaxException throws if host url is not valid.
-     * @throws IOException throws if anything happen during sending.
-     */
-    private HttpResponse sendGet(HttpRequest request) throws IOException, URISyntaxException {
-
-        URI uri = request.getUrl().toURI();
-        HttpGet requestGet = new HttpGet(uri);
-        setBasicHeaders(request, requestGet);
-
-        org.apache.http.HttpResponse responseGet = client.execute(requestGet);
-
-        HttpResponse response = HttpResponse.newBuilder()
-                .status(responseGet.getStatusLine().getStatusCode())
-                .headers(getHeaders(responseGet))
-                .body(getBody(responseGet.getEntity()))
-                .build();
-
-        EntityUtils.consume(responseGet.getEntity());
-
-        return response;
-    }
-
-    /**
-     * Sends POST request.
-     * @param request - virtual request.
-     * @return virtual response of the request.
-     * @throws URISyntaxException throws if host url is not valid.
-     * @throws IOException throws if anything happen during sending.
-     */
-    private HttpResponse sendPost(HttpRequest request) throws URISyntaxException, IOException {
-
-        URI uri = request.getUrl().toURI();
-        HttpPost requestPost = new HttpPost(uri);
-        setBasicHeaders(request, requestPost);
-
-        if (StringUtils.isNotBlank(request.getContentType())) {
-            requestPost.setHeader(CONTENT_TYPE, request.getContentType());
-        }
-
-        requestPost.setEntity(new StringEntity(request.getBody()));
-
-        org.apache.http.HttpResponse responsePost = client.execute(requestPost);
-
-        HttpResponse response = HttpResponse.newBuilder()
-                .headers(getHeaders(requestPost))
-                .status(responsePost.getStatusLine().getStatusCode())
-                .body(getBody(responsePost.getEntity()))
-                .build();
-
-        EntityUtils.consume(responsePost.getEntity());
-
-        return response;
-    }
-
-    /**
-     * Sets basic headers to each request.
-     * @param userRequest - virtual request.
-     * @param httpRequest - real request prepared to send.
-     */
-    private void setBasicHeaders(HttpRequest userRequest, HttpRequestBase httpRequest) {
-
-        httpRequest.setHeader(USER_AGENT, "SenslogConnector/1.0");
-        httpRequest.setHeader(CACHE_CONTROL, "no-cache");
-
-        for (Map.Entry<String, String> headerEntry : userRequest.getHeaders().entrySet()) {
-            httpRequest.setHeader(headerEntry.getKey(), headerEntry.getValue());
-        }
-    }
-
-    /**
-     * Returns map of headers from the response.
-     * @param response - response message.
-     * @return map of headers.
-     */
-    private Map<String, String> getHeaders(HttpMessage response) {
-        Map<String, String> headers = new HashMap<>();
-        for (Header header : response.getAllHeaders()) {
-            headers.put(header.getName(), header.getValue());
-        }
-        return headers;
-    }
-
-    /**
-     * Returns body from the response.
-     * @param entity - response entity.
-     * @return string body of the response.
-     * @throws IOException can not get body from the response.
-     */
-    private String getBody(HttpEntity entity) throws IOException {
-        if (entity == null) return "";
-        InputStream contentStream = entity.getContent();
-        InputStreamReader bodyStream = new InputStreamReader(contentStream);
-        BufferedReader rd = new BufferedReader(bodyStream);
-        StringBuilder bodyBuffer = new StringBuilder();
-        String line;
-        while ((line = rd.readLine()) != null) {
-            bodyBuffer.append(line);
-        }
-        return bodyBuffer.toString();
-    }
-}
+package cz.senslog.watchdog.util.http;
+
+import cz.senslog.watchdog.util.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpMessage;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.http.HttpHeaders.*;
+
+/**
+ * The class {@code HttpClient} represents a wrapper for {@link org.apache.http.client.HttpClient}.
+ * Provides functionality of sending GET and POST request. Otherwise is returned response with {@see #BAD_REQUEST}.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+public class HttpClient {
+
+    /** Instance of http client. */
+    private final org.apache.http.client.HttpClient client;
+
+    private final BasicCookieStore cookieStore;
+
+    /**
+     * Factory method to create a new instance of client.
+     * @return new instance of {@code HttpClient}.
+     */
+    public static HttpClient newHttpClient() {
+        return new HttpClient(HttpClientBuilder.create(), new BasicCookieStore());
+    }
+
+    /**
+     * Private constructors sets http client.
+     */
+    private HttpClient(HttpClientBuilder httpClientBuilder, BasicCookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+        this.client = httpClientBuilder.setDefaultCookieStore(cookieStore).build();
+    }
+
+    /**
+     * Sends http request.
+     * @param request - virtual request.
+     * @return virtual response.
+     */
+    public HttpResponse send(HttpRequest request) {
+        try {
+            switch (request.getMethod()) {
+                case GET:  return sendGet(request);
+                case POST: return sendPost(request);
+                default: return HttpResponse.newBuilder()
+                            .body("Request does not contain method definition.")
+                            .status(HttpCode.METHOD_NOT_ALLOWED).build();
+            }
+        } catch (URISyntaxException e) {
+            return HttpResponse.newBuilder()
+                    .body(e.getMessage()).status(HttpCode.BAD_REQUEST)
+                    .build();
+        } catch (IOException e) {
+            return  HttpResponse.newBuilder()
+                    .body(e.getMessage()).status(HttpCode.SERVER_ERROR)
+                    .build();
+        }
+    }
+
+    /**
+     * Sends GET request.
+     * @param request - virtual request.
+     * @return virtual response of the request.
+     * @throws URISyntaxException throws if host url is not valid.
+     * @throws IOException throws if anything happen during sending.
+     */
+    private HttpResponse sendGet(HttpRequest request) throws IOException, URISyntaxException {
+
+        URI uri = request.getUrl().toURI();
+        HttpGet requestGet = new HttpGet(uri);
+        setBasicHeaders(request, requestGet);
+
+        cookieStore.clear();
+        for (HttpCookie cookie : request.getCookies()) {
+            cookieStore.addCookie(cookie);
+        }
+
+        org.apache.http.HttpResponse responseGet = client.execute(requestGet);
+
+        HttpResponse response = HttpResponse.newBuilder()
+                .status(responseGet.getStatusLine().getStatusCode())
+                .headers(getHeaders(responseGet))
+                .body(getBody(responseGet.getEntity()))
+                .build();
+
+        EntityUtils.consume(responseGet.getEntity());
+
+        return response;
+    }
+
+    /**
+     * Sends POST request.
+     * @param request - virtual request.
+     * @return virtual response of the request.
+     * @throws URISyntaxException throws if host url is not valid.
+     * @throws IOException throws if anything happen during sending.
+     */
+    private HttpResponse sendPost(HttpRequest request) throws URISyntaxException, IOException {
+
+        URI uri = request.getUrl().toURI();
+        HttpPost requestPost = new HttpPost(uri);
+        setBasicHeaders(request, requestPost);
+
+        if (StringUtils.isNotBlank(request.getContentType())) {
+            requestPost.setHeader(CONTENT_TYPE, request.getContentType());
+        }
+
+        requestPost.setEntity(new StringEntity(request.getBody()));
+
+        cookieStore.clear();
+        for (HttpCookie cookie : request.getCookies()) {
+            cookieStore.addCookie(cookie);
+        }
+
+        org.apache.http.HttpResponse responsePost = client.execute(requestPost);
+
+        HttpResponse response = HttpResponse.newBuilder()
+                .headers(getHeaders(requestPost))
+                .status(responsePost.getStatusLine().getStatusCode())
+                .body(getBody(responsePost.getEntity()))
+                .build();
+
+        EntityUtils.consume(responsePost.getEntity());
+
+        return response;
+    }
+
+    /**
+     * Sets basic headers to each request.
+     * @param userRequest - virtual request.
+     * @param httpRequest - real request prepared to send.
+     */
+    private void setBasicHeaders(HttpRequest userRequest, HttpRequestBase httpRequest) {
+
+        httpRequest.setHeader(USER_AGENT, "SenslogConnector/1.0");
+        httpRequest.setHeader(CACHE_CONTROL, "no-cache");
+
+        for (Map.Entry<String, String> headerEntry : userRequest.getHeaders().entrySet()) {
+            httpRequest.setHeader(headerEntry.getKey(), headerEntry.getValue());
+        }
+    }
+
+    /**
+     * Returns map of headers from the response.
+     * @param response - response message.
+     * @return map of headers.
+     */
+    private Map<String, String> getHeaders(HttpMessage response) {
+        Map<String, String> headers = new HashMap<>();
+        for (Header header : response.getAllHeaders()) {
+            headers.put(header.getName(), header.getValue());
+        }
+        return headers;
+    }
+
+    /**
+     * Returns body from the response.
+     * @param entity - response entity.
+     * @return string body of the response.
+     * @throws IOException can not get body from the response.
+     */
+    private String getBody(HttpEntity entity) throws IOException {
+        if (entity == null) return "";
+        InputStream contentStream = entity.getContent();
+        InputStreamReader bodyStream = new InputStreamReader(contentStream);
+        BufferedReader rd = new BufferedReader(bodyStream);
+        StringBuilder bodyBuffer = new StringBuilder();
+        String line;
+        while ((line = rd.readLine()) != null) {
+            bodyBuffer.append(line);
+        }
+        return bodyBuffer.toString();
+    }
+}

+ 39 - 0
src/main/java/cz/senslog/watchdog/util/http/HttpCookie.java

@@ -0,0 +1,39 @@
+package cz.senslog.watchdog.util.http;
+
+import org.apache.http.impl.cookie.BasicClientCookie;
+
+public class HttpCookie extends BasicClientCookie {
+
+    public static HttpCookie empty() {
+        HttpCookie cookie = new HttpCookie("", "", "", "");
+        cookie.setSecure(false);
+        return cookie;
+    }
+
+    public HttpCookie(String name, String value, String domain, String path) {
+        super(name, value);
+        super.setDomain(domain);
+        super.setPath(path);
+        super.setSecure(true);
+    }
+
+    @Override
+    public String getName() {
+        return super.getName();
+    }
+
+    @Override
+    public String getValue() {
+        return super.getValue();
+    }
+
+    @Override
+    public String getDomain() {
+        return super.getDomain();
+    }
+
+    @Override
+    public String getPath() {
+        return super.getPath();
+    }
+}

+ 102 - 94
src/main/java/cz/senslog/watchdog/util/http/HttpRequest.java

@@ -1,94 +1,102 @@
-package cz.senslog.watchdog.util.http;
-
-import java.net.URL;
-import java.util.Map;
-
-/**
- * The class {@code HttpRequest} represents a wrapper for a http request.
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-public class HttpRequest {
-
-    public interface Builder {
-        Builder header(String name, String value);
-        Builder url(URL url);
-        Builder POST();
-        Builder GET();
-        Builder contentType(String contentType);
-        Builder body(String body);
-        HttpRequest build();
-    }
-
-    /**
-     * Factory method to create a new builder for {@link HttpRequest}.
-     * @return new instance of builder.
-     */
-    public static Builder newBuilder() {
-        return new HttpRequestBuilder();
-    }
-
-    /**
-     * Factory method to create a new builder for {@link HttpRequest}.
-     * @param url - host url.
-     * @return new instance of builder.
-     */
-    public static Builder newBuilder(URL url) {
-        HttpRequestBuilder builder = new HttpRequestBuilder();
-        builder.url(url);
-        return builder;
-    }
-
-    /** Request url. */
-    private final URL url;
-
-    /** Request headers. */
-    private final Map<String, String> headers;
-
-    /** Request body. */
-    private final String body;
-
-    /** Request method. */
-    private final HttpMethod method;
-
-    /** Request content type. */
-    private final String contentType;
-
-    /**
-     * Constructors sets all attributes.
-     * @param url - url.
-     * @param headers - headers.
-     * @param body - body.
-     * @param method - method.
-     * @param contentType - content type.
-     */
-    HttpRequest(URL url, Map<String, String> headers, String body, HttpMethod method, String contentType) {
-        this.url = url;
-        this.headers = headers;
-        this.body = body;
-        this.method = method;
-        this.contentType = contentType;
-    }
-
-    public URL getUrl() {
-        return url;
-    }
-
-    public String getBody() {
-        return body;
-    }
-
-    public HttpMethod getMethod() {
-        return method;
-    }
-
-    public Map<String, String> getHeaders() {
-        return headers;
-    }
-
-    public String getContentType() {
-        return contentType;
-    }
-}
+package cz.senslog.watchdog.util.http;
+
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * The class {@code HttpRequest} represents a wrapper for a http request.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+public class HttpRequest {
+
+    public interface Builder {
+        Builder header(String name, String value);
+        Builder url(URL url);
+        Builder POST();
+        Builder GET();
+        Builder contentType(String contentType);
+        Builder body(String body);
+        Builder addCookie(HttpCookie cookie);
+        HttpRequest build();
+    }
+
+    /**
+     * Factory method to create a new builder for {@link HttpRequest}.
+     * @return new instance of builder.
+     */
+    public static Builder newBuilder() {
+        return new HttpRequestBuilder();
+    }
+
+    /**
+     * Factory method to create a new builder for {@link HttpRequest}.
+     * @param url - host url.
+     * @return new instance of builder.
+     */
+    public static Builder newBuilder(URL url) {
+        HttpRequestBuilder builder = new HttpRequestBuilder();
+        builder.url(url);
+        return builder;
+    }
+
+    /** Request url. */
+    private final URL url;
+
+    /** Request headers. */
+    private final Map<String, String> headers;
+
+    /** Request body. */
+    private final String body;
+
+    /** Request method. */
+    private final HttpMethod method;
+
+    /** Request content type. */
+    private final String contentType;
+
+    private final HttpCookie [] cookies;
+
+    /**
+     * Constructors sets all attributes.
+     * @param url - url.
+     * @param headers - headers.
+     * @param body - body.
+     * @param method - method.
+     * @param contentType - content type.
+     */
+    HttpRequest(URL url, Map<String, String> headers, String body, HttpMethod method, String contentType, HttpCookie [] cookies) {
+        this.url = url;
+        this.headers = headers;
+        this.body = body;
+        this.method = method;
+        this.contentType = contentType;
+        this.cookies = cookies;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public HttpMethod getMethod() {
+        return method;
+    }
+
+    public Map<String, String> getHeaders() {
+        return headers;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public HttpCookie[] getCookies() {
+        return cookies;
+    }
+}

+ 79 - 69
src/main/java/cz/senslog/watchdog/util/http/HttpRequestBuilder.java

@@ -1,69 +1,79 @@
-package cz.senslog.watchdog.util.http;
-
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The class {@code HttpRequestBuilder} represents a builder for the {@link HttpRequest}.
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-final class HttpRequestBuilder implements HttpRequest.Builder {
-
-    private final Map<String, String> headers;
-    private URL url;
-    private String body;
-    private HttpMethod method;
-    private String contentType;
-
-    HttpRequestBuilder() {
-        this.headers = new HashMap<>();
-        this.method = HttpMethod.GET;
-        this.body = "";
-    }
-
-
-    @Override
-    public HttpRequest.Builder header(String name, String value) {
-        this.headers.put(name, value);
-        return this;
-    }
-
-    @Override
-    public HttpRequest.Builder url(URL url) {
-        this.url = url;
-        return this;
-    }
-
-    @Override
-    public HttpRequest.Builder POST() {
-        this.method = HttpMethod.POST;
-        return this;
-    }
-
-    @Override
-    public HttpRequest.Builder GET() {
-        this.method = HttpMethod.GET;
-        return this;
-    }
-
-    @Override
-    public HttpRequest.Builder contentType(String contentType) {
-        this.contentType = contentType;
-        return this;
-    }
-
-    @Override
-    public HttpRequest.Builder body(String body) {
-        this.body = body;
-        return this;
-    }
-
-    @Override
-    public HttpRequest build() {
-        return new HttpRequest(url, headers, body, method, contentType);
-    }
-}
+package cz.senslog.watchdog.util.http;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The class {@code HttpRequestBuilder} represents a builder for the {@link HttpRequest}.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+final class HttpRequestBuilder implements HttpRequest.Builder {
+
+    private final Map<String, String> headers;
+    private final List<HttpCookie> cookies;
+    private URL url;
+    private String body;
+    private HttpMethod method;
+    private String contentType;
+
+    HttpRequestBuilder() {
+        this.headers = new HashMap<>();
+        this.cookies = new ArrayList<>();
+        this.method = HttpMethod.GET;
+        this.body = "";
+    }
+
+
+    @Override
+    public HttpRequest.Builder header(String name, String value) {
+        this.headers.put(name, value);
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder url(URL url) {
+        this.url = url;
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder POST() {
+        this.method = HttpMethod.POST;
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder GET() {
+        this.method = HttpMethod.GET;
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder contentType(String contentType) {
+        this.contentType = contentType;
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder body(String body) {
+        this.body = body;
+        return this;
+    }
+
+    @Override
+    public HttpRequest.Builder addCookie(HttpCookie cookie) {
+        this.cookies.add(cookie);
+        return this;
+    }
+
+    @Override
+    public HttpRequest build() {
+        return new HttpRequest(url, headers, body, method, contentType, cookies.toArray(new HttpCookie[0]));
+    }
+}