Procházet zdrojové kódy

Added fetch module 'Alapans'

Lukas Cerny před 2 roky
rodič
revize
3bd1f34290
63 změnil soubory, kde provedl 1743 přidání a 472 odebrání
  1. 177 0
      README.md
  2. 37 0
      config/alapansSenslog.yaml
  3. 2 3
      connector-app/pom.xml
  4. 33 0
      connector-fetch-alapans/pom.xml
  5. 51 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansConfig.java
  6. 111 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansFetcher.java
  7. 99 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansProxySession.java
  8. 64 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansSession.java
  9. 54 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansSessionProxyConfig.java
  10. 44 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/ConnectorFetchAlapansProvider.java
  11. 7 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/Main.java
  12. 64 0
      connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/ResponseData.java
  13. 1 0
      connector-fetch-alapans/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider
  14. binární
      connector-fetch-alapans/src/main/resources/clienttruststore.jks
  15. 47 0
      connector-fetch-alapans/src/test/java/cz/senslog/connector/fetch/alapans/AlapansFetcherTest.java
  16. 1 7
      connector-fetch-api/pom.xml
  17. 1 2
      connector-fetch-azure/pom.xml
  18. 2 2
      connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/AzureFetcherTest.java
  19. 8 10
      connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/auth/AuthenticationServiceTest.java
  20. 33 0
      connector-fetch-demo/pom.xml
  21. 16 0
      connector-fetch-demo/src/main/java/cz/senslog/connector/fetch/demo/ConnectorFetchDemoProvider.java
  22. 1 0
      connector-fetch-demo/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider
  23. 1 2
      connector-fetch-drutes/pom.xml
  24. 1 2
      connector-fetch-fieldclimate/pom.xml
  25. 3 2
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcher.java
  26. 3 1
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/auth/AuthenticationService.java
  27. 1 2
      connector-fetch-senslog-v1/pom.xml
  28. 1 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcher.java
  29. 1 2
      connector-model/pom.xml
  30. 25 0
      connector-model/src/main/java/cz/senslog/connector/model/alapans/AlapansModel.java
  31. 23 0
      connector-model/src/main/java/cz/senslog/connector/model/alapans/LinkInfo.java
  32. 68 0
      connector-model/src/main/java/cz/senslog/connector/model/alapans/UnitData.java
  33. 3 0
      connector-model/src/main/java/cz/senslog/connector/model/config/ConnectorDescriptor.java
  34. 48 0
      connector-model/src/main/java/cz/senslog/connector/model/config/PropertyConfig.java
  35. 65 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/AlapansModelSenslogV1ModelConverter.java
  36. 1 3
      connector-model/src/main/java/cz/senslog/connector/model/converter/AzureModelSenslogV1ModelConverter.java
  37. 1 3
      connector-model/src/main/java/cz/senslog/connector/model/converter/AzureModelSenslogV2ModelConverter.java
  38. 2 2
      connector-model/src/main/java/cz/senslog/connector/model/converter/DrutesModelSenslogV1ModelConverter.java
  39. 1 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java
  40. 12 0
      connector-model/src/main/java/cz/senslog/connector/model/demo/DemoModel.java
  41. 3 5
      connector-model/src/main/java/cz/senslog/connector/model/v1/Observation.java
  42. 1 3
      connector-model/src/test/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverterTest.java
  43. 1 2
      connector-push-afarcloud/pom.xml
  44. 1 7
      connector-push-api/pom.xml
  45. 1 2
      connector-push-senslog-v1/pom.xml
  46. 0 0
      connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Config.java
  47. 38 38
      connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProvider.java
  48. 263 265
      connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Pusher.java
  49. 0 0
      connector-push-senslog-v1/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider
  50. 0 0
      connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConfigTest.java
  51. 0 0
      connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProviderTest.java
  52. 0 0
      connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1PusherTest.java
  53. 34 16
      connector-tools/pom.xml
  54. 106 47
      connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpClient.java
  55. 22 15
      connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpCookie.java
  56. 2 1
      connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpHeader.java
  57. 0 5
      connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpRequest.java
  58. 35 3
      connector-tools/src/main/java/cz/senslog/connector/tools/json/BasicJson.java
  59. 86 0
      connector-tools/src/main/java/cz/senslog/connector/tools/senslog/auth/SensLogAuthService.java
  60. 10 0
      connector-tools/src/main/java/cz/senslog/connector/tools/util/StringUtils.java
  61. 10 0
      docker-compose.yaml
  62. 1 0
      docker/Dockerfile
  63. 16 20
      pom.xml

+ 177 - 0
README.md

@@ -92,17 +92,194 @@ Usage: General Senslog Connector [options]
  
 ```
 
+## Creating a new module
+### Intellij Idea
+```markdown
+Right click on 'connector-period' -> New -> Module...
+```
+##### Module Settings
+Name convention for a new module
+```markdown
+pattern: connector-<fetch/push>-<module_name>
+example1: connector-fetch-demo
+example2: connector-push-demo
+```
+
+```markdown
+Build System: Maven
+Parent: General Senslog Connector
+```
+### Project Settings
+Edit **pom.xml** file in the new module. The example is made for the module with the name 'fetch-demo'. Change the name according to the new name of created module. Add following attributes.
+```markdown
+<artifactId>connector-fetch-demo</artifactId> # keep the generated name, should be chosen according to the name convention (see above)
+<name>fetch-demo</name> # change the name according to a new name
+<packaging>jar</packaging>
+
+<dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId> # change the artifact to fetch or push according to the type of the module
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+```
+Create a folder META-INF/services in resources to make following folder's structure:
+```markdown
+src/main/resources/META-INF/services
+```
+Create a file with the name:
+```java
+cz.senslog.connector.fetch.api.ConnectorFetchProvider
+```
 
+### Java Settings
+Create a package with following name convention (example is for fetch-demo):
+```java
+cz.senslog.connector.fetch.demo
+```
+Create a java class with the name (example is for fetch-demo):
+```java
+ConnectorFetchDemoProvider
+```
+Implement the interface in the Provider class: 
+```java
+cz.senslog.connector.fetch.api.ConnectorFetchProvider
+```
 
+Register the class (add following line) in the file in 'META-INF/services'
+```markdown
+cz.senslog.connector.fetch.demo.ConnectorFetchDemoProvider
+```
 
+### Config Settings
+Create a new file in the './config' directory with the following name convention:
+```markdown
+pattern: fetcherPusher.yaml
+example1: demoSenslog.yaml
+example2: myFetcherSenslog.yaml
+```
 
+Create a following structure:
+```yaml
+settings:
+    - Demo: # name of the fetcher module, e.g., Demo
+        name: "Demo fetcher"
+        provider: "cz.senslog.connector.fetch.demo.ConnectorFetchDemoProvider" 
+        # the same line as in the file META-INF/services/cz.senslog...ConnectorFetchProvider
 
+        # other attributes based on the module configuration
 
+    - SenslogV1: # name of the pusher module, e.g., SenslogV1
+        name: "Senslog V1"
+        provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
 
+        # other attributes based on the module configuration
 
+connectors:
+    - DemoSenslogV1:        # name of the connector, e.g., DemoSenslogV1
+        fetcher: "Demo"     # name of the fetcher from 'settings'
+        pusher: "SenslogV1" # name of the pusher from 'settings'
+        period: 86_400      # 24h 
+        startAt: "02:30:00" # hh:mm:ss  # non-mandatory attribute
+        initDelay: 5        # non-mandatory attribute
+```
 
+### Maven Settings
+Open the parent 'pom.xml' file of the project.
 
+Check if the new module is registered in the following section:
+```xml
+<modules>
+    ...
+    <module>connector-fetch-demo</module>
+</modules>
+```
+Create a new connector profile (the example is for creating a connector for Demo and Senslog):
+```xml
+<profiles>
+    ...
+    <profile>
+        <id>DemoSenslog1</id>                           # profile identifier
+        <modules>
+            <module>connector-fetch-demo</module>       # name of the fetch module
+            <module>connector-push-senslog-v1</module>  # name of the push module
+        </modules>
+    </profile>
+</profiles>
+```
+Add the new module to the profile with the ID 'all'
+```xml
+<profile>
+    <id>all</id>
+    ...
+    <modules>
+        ....
+        <module>connector-fetch-demo</module>
+    </modules>
+</profile>
+```
 
+### Docker Settings
+Register new service in the 'docker-compose.yaml' file:
+```yaml
+  demoConnector:                            # id of the service
+    container_name: demoSenslog1            # name of the running container
+    build:
+      dockerfile: docker/Dockerfile
+      context: .
+      args:
+         MAVEN_PROFILE: DemoSenslog1        # name of the maven profile to build the connector
+    ports:                                  # non-mandatory port for debugging
+      - "5005:5005"
+    restart: always                       
+    environment:
+        APP_PARAMS: -cf config/demoSenslog.yaml # path to the config
+        DEBUG: "false"                      # non-mandatory param for debugging true/false
+```
+### Create a model
+Create a new package in the module 'connector-model' with the following name convention:
+```markdown
+cz.senslog.connector.model.demo
+```
+Create a new class in the package with the name:
+```markdown
+DemoModel
+```
 
+... and inherit the class AbstractModel:
+```java
+public class DemoModel extends AbstractModel {
+    protected DemoModel(OffsetDateTime from, OffsetDateTime to) {
+        super(from, to);
+    }  
+}
+```
+Create a new model converter in the package 'converter', e.g. for the Demo and Senslog,
 
+```java
+public final class DemoModelSenslogV1ModelConverter implements Converter<DemoModel, SenslogV1Model> {
+    @Override
+    public SenslogV1Model convert(DemoModel inputModel) {
+        return null;
+    }
+}
+```
+Register the converter in the class:
+```java
+cz.senslog.connector.model.converter.ModelConverterProvider
+```
+... in the method **config()**:
+```java
+register(DemoModelSenslogV1ModelConverter.class);
+```
 

+ 37 - 0
config/alapansSenslog.yaml

@@ -0,0 +1,37 @@
+api1: &alapansApi
+  baseUrl: "https://gca44347dc41d4c-alapansdw.adb.eu-paris-1.oraclecloudapps.com/ords/alapans/sensors/date"
+
+api2: &senslogApi
+  baseUrl: "https://sensor.lesprojekt.cz/senslog15/"
+
+settings:
+  - Alapans:
+      name: "Alapans fetcher"
+      provider: "cz.senslog.connector.fetch.alapans.ConnectorFetchAlapansProvider"
+      <<: *alapansApi
+
+      timeZone: "Europe/Riga"
+      startDate: "2023-05-29" # date time to start fetching
+      newDataExecutionTime: "06:30:00"  # time to start new period of fetch
+      period: 1 # nm of days to fetch
+
+      sessionProxy:
+        <<: *senslogApi
+        user: "vestiena"
+        group: "vestiena"
+        senslogAuth:
+          username: "vestiena"
+          password: "potato"
+
+  - SenslogV1: # name of the pusher module, e.g., SenslogV1
+      name: "Senslog V1"
+      provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
+      <<: *senslogApi
+
+connectors:
+  - AlapansSenslogV1:
+      fetcher: "Alapans"
+      pusher: "SenslogV1"
+      period: 3600      # 600 = 10min
+#      startAt: "06:30:00" # hh:mm:ss  # non-mandatory attribute
+      initDelay: 5        # non-mandatory attribute

+ 2 - 3
connector-app/pom.xml

@@ -3,14 +3,13 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>connector-app</artifactId>
-    <version>${project.parent.version}</version>
     <name>app</name>
 
     <dependencies>
@@ -28,7 +27,7 @@
         <dependency>
             <groupId>com.beust</groupId>
             <artifactId>jcommander</artifactId>
-            <version>1.72</version>
+            <version>1.82</version>
         </dependency>
 
     </dependencies>

+ 33 - 0
connector-fetch-alapans/pom.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cz.senslog</groupId>
+        <artifactId>connector-period</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>connector-fetch-alapans</artifactId>
+    <name>fetch-alapans</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 51 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansConfig.java

@@ -0,0 +1,51 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.TimeZone;
+
+public class AlapansConfig {
+    private final String baseUrl;
+    private final TimeZone timeZone;
+    private final LocalDate startDate;
+    private final int period;
+
+    private final LocalTime newDataExecutionTime;
+    private final AlapansSessionProxyConfig sessionProxyConfig;
+    public AlapansConfig(DefaultConfig defaultConfig) {
+        this.baseUrl = defaultConfig.getStringProperty("baseUrl");
+        this.sessionProxyConfig = defaultConfig.containsProperty("sessionProxy") ?
+                new AlapansSessionProxyConfig(defaultConfig.getPropertyConfig("sessionProxy")) : null;
+        this.timeZone = TimeZone.getTimeZone(defaultConfig.getStringProperty("timeZone"));
+        this.startDate = defaultConfig.getLocalDateProperty("startDate");
+        this.newDataExecutionTime = defaultConfig.getLocalTimeProperty("newDataExecutionTime");
+        this.period = defaultConfig.getIntegerProperty("period");
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public AlapansSessionProxyConfig getSessionProxy() {
+        return sessionProxyConfig;
+    }
+
+    public TimeZone getTimeZone() {
+        return timeZone;
+    }
+
+    public LocalDate getStartDate() {
+        return startDate;
+    }
+
+    public int getPeriod() {
+        return period;
+    }
+
+    public LocalTime getNewDataExecutionTime() {
+        return newDataExecutionTime;
+    }
+}

+ 111 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansFetcher.java

@@ -0,0 +1,111 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.model.alapans.AlapansModel;
+import cz.senslog.connector.model.alapans.UnitData;
+import cz.senslog.connector.tools.http.HttpClient;
+import cz.senslog.connector.tools.http.HttpRequest;
+import cz.senslog.connector.tools.http.HttpResponse;
+import cz.senslog.connector.tools.http.URLBuilder;
+import cz.senslog.connector.tools.util.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.function.Function;
+
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+import static java.lang.String.format;
+
+public class AlapansFetcher implements ConnectorFetcher<AlapansSession, AlapansModel> {
+
+    private static final Logger logger = LogManager.getLogger(AlapansFetcher.class);
+
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("MMMddyyyy");
+
+    private final AlapansConfig config;
+
+    private final HttpClient httpClient;
+
+    public AlapansFetcher(AlapansConfig config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+    }
+
+    public AlapansFetcher() {
+        this(null, null);
+    }
+
+    @Override
+    public void init() throws Exception {}
+
+    @Override
+    public AlapansModel fetch(Optional<AlapansSession> sessionOpt) {
+
+        AlapansSession session = sessionOpt.orElse(AlapansSession.emptySession());
+
+        LocalDate fromDate = sessionOpt.map(AlapansSession::getFromDate).orElse(config.getStartDate());
+        LocalDate toDate = sessionOpt.map(AlapansSession::getToDate).orElse(fromDate.plusDays(config.getPeriod()));
+
+        if (session.isEndOfIntervalIteration() &&
+                (toDate.equals(LocalDate.now()) && !config.getNewDataExecutionTime().isBefore(LocalTime.now()))
+        ) {
+            return AlapansModel.empty();
+        }
+
+        ZoneId zoneId = config.getTimeZone().toZoneId();
+        OffsetDateTime fromWithOffset = ZonedDateTime.of(LocalDateTime.of(fromDate, LocalTime.MIN), zoneId).toOffsetDateTime();
+        OffsetDateTime toWithOffset = ZonedDateTime.of(LocalDateTime.of(toDate, LocalTime.MIN), zoneId).toOffsetDateTime();
+
+        String intervalParam = String.format("%s/%s", fromDate.format(DATE_FORMATTER), toDate.format(DATE_FORMATTER));
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder
+                        .newBuilder(config.getBaseUrl(), intervalParam)
+                        .addParam("offset", session.getNextOffset())
+                        .build())
+                .build();
+
+        HttpResponse response = httpClient.send(request);
+
+        if (response.isError()) {
+            logger.error(format("Can not get information about the sensors. %s", response.getBody()));
+        } else {
+            final Function<String, Double> PARSE_DOUBLE = s -> s != null ? Double.parseDouble(s) : null;
+
+            ResponseData responseData = jsonToObject(response.getBody(), ResponseData.class);
+            logger.info("Fetched {} records at the interval {} with the offset {}.", responseData.getCount(), intervalParam, session.getNextOffset());
+            List<UnitData> unitDataList = new ArrayList<>(responseData.getCount());
+            for (Map<String, String> item : responseData.getItems()) {
+                unitDataList.add(new UnitData(
+                        ZonedDateTime.of(LocalDateTime.parse(StringUtils.substringcut(item.get("s_date"), 1)), zoneId).toOffsetDateTime(),
+                        item.get("device id"),
+                        PARSE_DOUBLE.apply(item.get("air temperature")),
+                        PARSE_DOUBLE.apply(item.get("air humidity")),
+                        PARSE_DOUBLE.apply(item.get("soil temperature 1")),
+                        PARSE_DOUBLE.apply(item.get("soil temperature 2")),
+                        PARSE_DOUBLE.apply(item.get("soil moisture 1")),
+                        PARSE_DOUBLE.apply(item.get("soil moisture 2")),
+                        PARSE_DOUBLE.apply(item.get("battery voltage"))
+                ));
+            }
+
+            if (responseData.isHasMore()) {
+                session.setHasMore(true);
+                session.setNextOffset(responseData.getOffset() + responseData.getLimit());
+                session.setFromDate(fromDate);
+                session.setToDate(toDate);
+            } else {
+                session.setHasMore(false);
+                session.setNextOffset(0);
+                session.setFromDate(toDate);
+                session.setToDate(toDate.plusDays(config.getPeriod()));
+            }
+
+            return new AlapansModel(fromWithOffset, toWithOffset, unitDataList);
+        }
+
+        return new AlapansModel(fromWithOffset, toWithOffset, Collections.emptyList());
+    }
+}

+ 99 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansProxySession.java

@@ -0,0 +1,99 @@
+package cz.senslog.connector.fetch.alapans;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.FetchProxySession;
+import cz.senslog.connector.model.alapans.AlapansModel;
+import cz.senslog.connector.tools.http.*;
+import cz.senslog.connector.tools.senslog.auth.SensLogAuthService;
+import cz.senslog.connector.tools.util.Tuple;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+import static java.time.OffsetDateTime.parse;
+import static java.time.format.DateTimeFormatter.ofPattern;
+
+public class AlapansProxySession extends FetchProxySession<AlapansSession, AlapansModel> {
+
+    private static final Logger logger = LogManager.getLogger(AlapansProxySession.class);
+
+    protected static class ObservationInfo { OffsetDateTime timeStamp; }
+
+    private static final int ALAPANS_API_INTERVAL_DAYS = 1;
+
+    private final HttpClient httpClient;
+
+    private final AlapansSessionProxyConfig config;
+
+    private final SensLogAuthService sensLogAuthService;
+
+    public AlapansProxySession(
+            ConnectorFetcher<AlapansSession, AlapansModel> fetcher,
+            AlapansSessionProxyConfig config,
+            HttpClient httpClient
+    ) {
+        super(fetcher);
+        this.httpClient = httpClient;
+        this.config = config;
+        this.sensLogAuthService = SensLogAuthService.getInstance(config.getBaseUrl());
+    }
+
+    @Override
+    protected AlapansSession preProcessing(Optional<AlapansSession> previousSession) {
+        AlapansSession session = previousSession.orElseGet(() -> new AlapansSession(true));
+        if (session.isNotInitialized()) {
+
+            HttpCookie authCookie = sensLogAuthService.getAuthCookie(
+                    config.getSenslogAuth().getUsername(),
+                    config.getSenslogAuth().getPassword()
+            );
+
+            if (!authCookie.isSecure()) {
+                logger.warn("Auth cookie is not valid to be used.");
+                return session;
+            }
+
+            HttpRequest request = HttpRequest.newBuilder().GET()
+                    .url(URLBuilder.newBuilder(config.getBaseUrl(), "SensorService")
+                            .addParam("Operation", "GetLastObservations")
+                            .addParam("group", config.getGroup())
+                            .addParam("user", config.getUser())
+                            .build())
+                    .addCookie(authCookie)
+                    .build();
+            HttpResponse response = httpClient.send(request);
+            if (response.isOk()) {
+                Type lastObsType = new TypeToken<Collection<ObservationInfo>>() {}.getType();
+                List<ObservationInfo> lastObservations = jsonToObject(response.getBody(), lastObsType,
+                        Tuple.of(OffsetDateTime.class, el -> parse(el, ofPattern("yyyy-MM-dd HH:mm:ssX")))
+                );
+
+                if (!lastObservations.isEmpty()) {
+                    LocalDate lastDate = LocalDate.MIN;
+                    for (ObservationInfo info : lastObservations) {
+                        LocalDate tempDate = info.timeStamp.toLocalDate();
+                        if (tempDate.isAfter(lastDate)) {
+                            lastDate = tempDate;
+                        }
+                    }
+                    session.setFromDate(lastDate.plusDays(ALAPANS_API_INTERVAL_DAYS));
+                }
+            } else {
+                logger.error("Can not get data from the server {}. Error {} {}",
+                        config.getBaseUrl(), response.getStatus(), response.getBody());
+            }
+        }
+        return session;
+    }
+
+    @Override
+    protected void postProcessing(Optional<AlapansModel> model, Optional<AlapansSession> session) {}
+}

+ 64 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansSession.java

@@ -0,0 +1,64 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.time.LocalDate;
+
+public class AlapansSession extends ProxySessionModel {
+
+    private int nextOffset;
+
+    private Boolean hasMore;
+
+    private LocalDate fromDate, toDate;
+
+    public static AlapansSession emptySession() {
+        return new AlapansSession(false);
+    }
+
+    AlapansSession(boolean isActive) {
+        super(isActive);
+        this.nextOffset = 0;
+        this.hasMore = null;
+    }
+
+    public LocalDate getFromDate() {
+        return fromDate;
+    }
+
+    public void setFromDate(LocalDate fromDate) {
+        this.fromDate = fromDate;
+    }
+
+    public LocalDate getToDate() {
+        return toDate;
+    }
+
+    public void setToDate(LocalDate toDate) {
+        this.toDate = toDate;
+    }
+
+    public int getNextOffset() {
+        return nextOffset;
+    }
+
+    public void setNextOffset(int nextOffset) {
+        this.nextOffset = nextOffset;
+    }
+
+    public Boolean hasNext() {
+        return hasMore;
+    }
+
+    public boolean isNotInitialized() {
+        return hasMore == null;
+    }
+
+    public boolean isEndOfIntervalIteration() {
+        return nextOffset == 0 && hasMore != null && !hasMore && toDate != null && fromDate != null;
+    }
+
+    public void setHasMore(Boolean hasMore) {
+        this.hasMore = hasMore;
+    }
+}

+ 54 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/AlapansSessionProxyConfig.java

@@ -0,0 +1,54 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.model.config.PropertyConfig;
+
+public class AlapansSessionProxyConfig {
+
+    public static class SenslogAuth {
+        private final String username, password;
+
+        SenslogAuth(String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+    }
+
+    private final String baseUrl;
+
+    private final String user;
+    private final String group;
+
+    private final SenslogAuth senslogAuth;
+
+    public AlapansSessionProxyConfig(PropertyConfig defaultConfig) {
+        this.baseUrl = defaultConfig.getStringProperty("baseUrl");
+        this.user = defaultConfig.getStringProperty("user");
+        this.group = defaultConfig.getStringProperty("group");
+        PropertyConfig sensogAuth = defaultConfig.getPropertyConfig("senslogAuth");
+        this.senslogAuth = new SenslogAuth(sensogAuth.getStringProperty("username"), sensogAuth.getStringProperty("password"));
+    }
+
+    public SenslogAuth getSenslogAuth() {
+        return senslogAuth;
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+}

+ 44 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/ConnectorFetchAlapansProvider.java

@@ -0,0 +1,44 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.alapans.AlapansModel;
+import cz.senslog.connector.model.config.DefaultConfig;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
+import static cz.senslog.connector.tools.http.HttpClient.newHttpSSLClient;
+
+public class ConnectorFetchAlapansProvider implements cz.senslog.connector.fetch.api.ConnectorFetchProvider {
+
+    private static final Logger logger = LogManager.getLogger(ConnectorFetchAlapansProvider.class);
+
+    @Override
+    public ExecutableFetcher<AlapansModel> createExecutableFetcher(DefaultConfig defaultConfig) {
+        logger.info("Initialization a new fetch provider {}.", ConnectorFetchAlapansProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        AlapansConfig config = new AlapansConfig(defaultConfig);
+        logger.info("Configuration for {} was created successfully.", ConnectorFetcher.class);
+
+        logger.debug("Creating a new instance of {}.", AlapansFetcher.class);
+        AlapansFetcher fetcher = new AlapansFetcher(config, newHttpSSLClient());
+
+        logger.debug("Getting a configuration for proxy session.");
+        AlapansSessionProxyConfig proxyConfig = config.getSessionProxy();
+
+        ExecutableFetcher<AlapansModel> executor;
+        if (proxyConfig != null) {
+            logger.debug("Creating a new instance of {}.", AlapansProxySession.class);
+            AlapansProxySession proxySession = new AlapansProxySession(fetcher, proxyConfig, newHttpClient());
+            logger.info("Fetcher session for {} was created successfully.", AlapansProxySession.class);
+            executor = ExecutableFetcher.createWithProxySession(proxySession);
+        } else {
+            executor = ExecutableFetcher.create(fetcher);
+        }
+        logger.info("Fetcher executor for {} was created successfully.", AlapansFetcher.class);
+
+        return executor;
+    }
+}

+ 7 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/Main.java

@@ -0,0 +1,7 @@
+package cz.senslog.connector.fetch.alapans;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("Hello world!");
+    }
+}

+ 64 - 0
connector-fetch-alapans/src/main/java/cz/senslog/connector/fetch/alapans/ResponseData.java

@@ -0,0 +1,64 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.model.alapans.LinkInfo;
+
+import java.util.List;
+import java.util.Map;
+
+public class ResponseData {
+
+    private List<Map<String, String>> items;
+    private boolean hasMore;
+    private int limit;
+    private int offset;
+    private int count;
+    private List<LinkInfo> links;
+
+    public List<Map<String, String>> getItems() {
+        return items;
+    }
+
+    public void setItems(List<Map<String, String>> items) {
+        this.items = items;
+    }
+
+    public boolean isHasMore() {
+        return hasMore;
+    }
+
+    public void setHasMore(boolean hasMore) {
+        this.hasMore = hasMore;
+    }
+
+    public int getLimit() {
+        return limit;
+    }
+
+    public void setLimit(int limit) {
+        this.limit = limit;
+    }
+
+    public int getOffset() {
+        return offset;
+    }
+
+    public void setOffset(int offset) {
+        this.offset = offset;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public List<LinkInfo> getLinks() {
+        return links;
+    }
+
+    public void setLinks(List<LinkInfo> links) {
+        this.links = links;
+    }
+}

+ 1 - 0
connector-fetch-alapans/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider

@@ -0,0 +1 @@
+cz.senslog.connector.fetch.alapans.ConnectorFetchAlapansProvider

binární
connector-fetch-alapans/src/main/resources/clienttruststore.jks


+ 47 - 0
connector-fetch-alapans/src/test/java/cz/senslog/connector/fetch/alapans/AlapansFetcherTest.java

@@ -0,0 +1,47 @@
+package cz.senslog.connector.fetch.alapans;
+
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.alapans.AlapansModel;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.converter.AlapansModelSenslogV1ModelConverter;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AlapansFetcherTest {
+
+    @Test
+    void online_fetch() {
+        DefaultConfig defaultConfig = new DefaultConfig("", null);
+
+        defaultConfig.setProperty("baseUrl", "https://gca44347dc41d4c-alapansdw.adb.eu-paris-1.oraclecloudapps.com/ords/alapans/sensors/date");
+        defaultConfig.setProperty("timeZone", "Europe/Riga");
+        defaultConfig.setProperty("startDate", "2023-05-29");
+        defaultConfig.setProperty("newDataExecutionTime", "06:30:00");
+        defaultConfig.setProperty("period", 1);
+        defaultConfig.setProperty("sessionProxy", new HashMap<String, Object>() {{
+            put("baseUrl", "https://sensor.lesprojekt.cz/senslog15/");
+            put("user", "vestiena");
+            put("group", "vestiena");
+            put("senslogAuth", new HashMap<String, Object>() {{
+                put("username", "vestiena");
+                put("password", "potato");
+            }});
+        }});
+
+        ExecutableFetcher<AlapansModel> fetcher = new ConnectorFetchAlapansProvider()
+                .createExecutableFetcher(defaultConfig);
+
+        AlapansModelSenslogV1ModelConverter converter = new AlapansModelSenslogV1ModelConverter();
+
+
+        System.out.println(converter.convert(fetcher.execute()).getObservations().size());
+        System.out.println(converter.convert(fetcher.execute()).getObservations().size());
+        System.out.println(converter.convert(fetcher.execute()).getObservations().size());
+        System.out.println(converter.convert(fetcher.execute()).getObservations().size());
+        System.out.println(converter.convert(fetcher.execute()).getObservations().size());
+    }
+}

+ 1 - 7
connector-fetch-api/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,17 +11,11 @@
 
     <artifactId>connector-fetch-api</artifactId>
     <name>fetch-api</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
             <groupId>cz.senslog</groupId>
-            <artifactId>connector-tools</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>cz.senslog</groupId>
             <artifactId>connector-model</artifactId>
             <version>${project.parent.version}</version>
         </dependency>

+ 1 - 2
connector-fetch-azure/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-fetch-azure</artifactId>
     <name>fetch-azure</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 2 - 2
connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/AzureFetcherTest.java

@@ -24,6 +24,7 @@ import static cz.senslog.connector.tools.http.HttpCode.SERVER_ERROR;
 import static cz.senslog.connector.tools.json.BasicJson.objectToJson;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -132,14 +133,13 @@ class AzureFetcherTest {
     void fetch_ErrorSensorsInfoRequest_EmptySensors() {
 
         HttpClient httpClient = mock(HttpClient.class);
-        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
+        when(httpClient.send(any())).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
             HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
             String path = request.getUrl().getPath();
             if (path.equals("/sensorInfo")) {
                 HttpResponse.newBuilder().status(SERVER_ERROR).build();
             }
             return HttpResponse.newBuilder().status(SERVER_ERROR).build();
-
         });
 
         AuthenticationService authService = mock(AuthenticationService.class);

+ 8 - 10
connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/auth/AuthenticationServiceTest.java

@@ -27,16 +27,14 @@ class AuthenticationServiceTest {
     void getAccessToken_Valid_Response_True() {
 
         HttpClient httpClient = mock(HttpClient.class);
-        when(httpClient.send(any(HttpRequest.class))).thenAnswer(new Answer<HttpResponse>() {
-            @Override public HttpResponse answer(InvocationOnMock invocationOnMock) {
-                AzureAuthorizationInfo authInfo = new AzureAuthorizationInfo();
-                authInfo.setExpires(LocalDateTime.now().plusDays(1));
-                authInfo.setAccessToken("#12345");
-                List<AzureAuthorizationInfo> azureInfos = new ArrayList<>(1);
-                azureInfos.add(authInfo);
-                return HttpResponse.newBuilder()
-                        .status(HttpCode.OK).body(objectToJson(azureInfos)).build();
-            }
+        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
+            AzureAuthorizationInfo authInfo = new AzureAuthorizationInfo();
+            authInfo.setExpires(LocalDateTime.now().plusDays(1));
+            authInfo.setAccessToken("#12345");
+            List<AzureAuthorizationInfo> azureInfos = new ArrayList<>(1);
+            azureInfos.add(authInfo);
+            return HttpResponse.newBuilder()
+                    .status(HttpCode.OK).body(objectToJson(azureInfos)).build();
         });
 
         DefaultConfig defaultConfig = new DefaultConfig("auth", any());

+ 33 - 0
connector-fetch-demo/pom.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cz.senslog</groupId>
+        <artifactId>connector-period</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>connector-fetch-demo</artifactId>
+    <name>fetch-demo</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 16 - 0
connector-fetch-demo/src/main/java/cz/senslog/connector/fetch/demo/ConnectorFetchDemoProvider.java

@@ -0,0 +1,16 @@
+package cz.senslog.connector.fetch.demo;
+
+import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.api.AbstractModel;
+import cz.senslog.connector.model.config.DefaultConfig;
+
+public class ConnectorFetchDemoProvider implements ConnectorFetchProvider  {
+
+    @Override
+    public ExecutableFetcher<? extends AbstractModel> createExecutableFetcher(DefaultConfig defaultConfig) {
+        throw new RuntimeException(
+                "This is a demo module used as an inspiration for creating other modules."
+        );
+    }
+}

+ 1 - 0
connector-fetch-demo/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider

@@ -0,0 +1 @@
+cz.senslog.connector.fetch.demo.ConnectorFetchDemoProvider

+ 1 - 2
connector-fetch-drutes/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-fetch-drutes</artifactId>
     <name>fetch-drutes</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 1 - 2
connector-fetch-fieldclimate/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-fetch-fieldclimate</artifactId>
     <name>fetch-fieldclimate</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 3 - 2
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcher.java

@@ -32,7 +32,8 @@ import static java.time.OffsetDateTime.MIN;
 import static java.time.ZoneOffset.UTC;
 import static java.time.format.DateTimeFormatter.*;
 import static java.util.Collections.emptyList;
-import static org.apache.http.client.utils.DateUtils.formatDate;
+import static org.apache.hc.client5.http.utils.DateUtils.FORMATTER_RFC1123;
+import static org.apache.hc.client5.http.utils.DateUtils.formatDate;
 
 public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateSession, FieldClimateModel> {
 
@@ -58,7 +59,7 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateSession
     public void init() throws Exception {
         assert config != null; assert  authService != null; assert  httpClient != null;
 
-        String requestDate = formatDate(new Date());
+        String requestDate = formatDate(new Date().toInstant(), FORMATTER_RFC1123);
         HostConfig stationsHost = config.getStationsHost();
         String seed = "GET" + stationsHost.getPath() + requestDate;
 

+ 3 - 1
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/auth/AuthenticationService.java

@@ -1,6 +1,8 @@
 package cz.senslog.connector.fetch.fieldclimate.auth;
 
-import org.apache.commons.codec.binary.Hex;
+
+
+import org.apache.hc.client5.http.utils.Hex;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;

+ 1 - 2
connector-fetch-senslog-v1/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-fetch-senslog-v1</artifactId>
     <name>fetch-senslog-v1</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 1 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcher.java

@@ -4,6 +4,7 @@
 package cz.senslog.connector.fetch.senslog.v1;
 
 import com.google.gson.reflect.TypeToken;
+import cz.senslog.connector.model.v1.Record;
 import cz.senslog.connector.tools.http.HttpClient;
 import cz.senslog.connector.tools.http.HttpRequest;
 import cz.senslog.connector.tools.http.HttpResponse;

+ 1 - 2
connector-model/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-model</artifactId>
     <name>model</name>
-    <version>${project.parent.version}</version>
 
     <dependencies>
         <dependency>

+ 25 - 0
connector-model/src/main/java/cz/senslog/connector/model/alapans/AlapansModel.java

@@ -0,0 +1,25 @@
+package cz.senslog.connector.model.alapans;
+
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+import java.util.Collections;
+import java.util.List;
+
+public class AlapansModel extends AbstractModel {
+
+    private final List<UnitData> unitData;
+
+    public static AlapansModel empty() {
+        return new AlapansModel(OffsetDateTime.MIN, OffsetDateTime.MIN, Collections.emptyList());
+    }
+
+    public AlapansModel(OffsetDateTime from, OffsetDateTime to, List<UnitData> unitData) {
+        super(from, to);
+        this.unitData = unitData;
+    }
+
+    public List<UnitData> getUnitData() {
+        return unitData;
+    }
+}

+ 23 - 0
connector-model/src/main/java/cz/senslog/connector/model/alapans/LinkInfo.java

@@ -0,0 +1,23 @@
+package cz.senslog.connector.model.alapans;
+
+public class LinkInfo {
+
+    private String rel;
+    private String href;
+
+    public String getRel() {
+        return rel;
+    }
+
+    public void setRel(String rel) {
+        this.rel = rel;
+    }
+
+    public String getHref() {
+        return href;
+    }
+
+    public void setHref(String href) {
+        this.href = href;
+    }
+}

+ 68 - 0
connector-model/src/main/java/cz/senslog/connector/model/alapans/UnitData.java

@@ -0,0 +1,68 @@
+package cz.senslog.connector.model.alapans;
+
+import java.time.OffsetDateTime;
+
+public class UnitData {
+
+    private final OffsetDateTime timestamp;
+    private final String deviceId;
+    private final Double airTemperature;
+    private final Double airHumidity;
+    private final Double soilTemperature1;
+    private final Double soilTemperature2;
+    private final Double soilMoisture1;
+    private final Double soilMoisture2;
+
+    private final Double batteryVoltage;
+
+    public UnitData(OffsetDateTime timestamp, String deviceId,
+                    Double airTemperature, Double airHumidity,
+                    Double soilTemperature1, Double soilTemperature2,
+                    Double soilMoisture1, Double soilMoisture2, Double batteryVoltage) {
+        this.timestamp = timestamp;
+        this.deviceId = deviceId;
+        this.airTemperature = airTemperature;
+        this.airHumidity = airHumidity;
+        this.soilTemperature1 = soilTemperature1;
+        this.soilTemperature2 = soilTemperature2;
+        this.soilMoisture1 = soilMoisture1;
+        this.soilMoisture2 = soilMoisture2;
+        this.batteryVoltage = batteryVoltage;
+    }
+
+    public OffsetDateTime getTimestamp() {
+        return timestamp;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public Double getAirTemperature() {
+        return airTemperature;
+    }
+
+    public Double getAirHumidity() {
+        return airHumidity;
+    }
+
+    public Double getSoilTemperature1() {
+        return soilTemperature1;
+    }
+
+    public Double getSoilTemperature2() {
+        return soilTemperature2;
+    }
+
+    public Double getSoilMoisture1() {
+        return soilMoisture1;
+    }
+
+    public Double getSoilMoisture2() {
+        return soilMoisture2;
+    }
+
+    public Double getBatteryVoltage() {
+        return batteryVoltage;
+    }
+}

+ 3 - 0
connector-model/src/main/java/cz/senslog/connector/model/config/ConnectorDescriptor.java

@@ -3,6 +3,9 @@
 
 package cz.senslog.connector.model.config;
 
+import cz.senslog.connector.tools.json.FormatFunction;
+import cz.senslog.connector.tools.util.Tuple;
+
 import java.time.LocalTime;
 
 import static cz.senslog.connector.tools.json.BasicJson.objectToJson;

+ 48 - 0
connector-model/src/main/java/cz/senslog/connector/model/config/PropertyConfig.java

@@ -7,7 +7,9 @@ import cz.senslog.connector.tools.exception.PropertyNotFoundException;
 import cz.senslog.connector.tools.util.ClassUtils;
 import cz.senslog.connector.tools.util.LocalDateTimeUtils;
 
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -132,6 +134,52 @@ public class PropertyConfig {
     }
 
     /**
+     * Returns property as a LocalTime.
+     * @param name - name of property.
+     * @return localTime value.
+     */
+    public LocalTime getLocalTimeProperty(String name) {
+        Object object = getProperty(name);
+
+        if (object instanceof LocalTime) {
+            return (LocalTime) object;
+        } else if (object instanceof Date) {
+            Date date = (Date) object;
+            return date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalTime();
+        } else if (object instanceof String) {
+            return LocalTime.parse((String)object, DateTimeFormatter.ISO_TIME);
+
+        } else {
+            throw new ClassCastException(format(
+                    "Property '%s' can not be cast to %s", getNewPropertyId(name), LocalTime.class)
+            );
+        }
+    }
+
+    /**
+     * Returns property as a LocalTime.
+     * @param name - name of property.
+     * @return localTime value.
+     */
+    public LocalDate getLocalDateProperty(String name) {
+        Object object = getProperty(name);
+
+        if (object instanceof LocalDate) {
+            return (LocalDate) object;
+        } else if (object instanceof Date) {
+            Date date = (Date) object;
+            return date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalDate();
+        } else if (object instanceof String) {
+            return LocalDate.parse((String)object, DateTimeFormatter.ISO_DATE);
+
+        } else {
+            throw new ClassCastException(format(
+                    "Property '%s' can not be cast to %s", getNewPropertyId(name), LocalTime.class)
+            );
+        }
+    }
+
+    /**
      * Returns property as a optional of LocalDateTime
      * @param name - name of property.
      * @return optional of localDateTime value.

+ 65 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/AlapansModelSenslogV1ModelConverter.java

@@ -0,0 +1,65 @@
+package cz.senslog.connector.model.converter;
+
+import cz.senslog.connector.model.alapans.AlapansModel;
+import cz.senslog.connector.model.alapans.UnitData;
+import cz.senslog.connector.model.api.Converter;
+import cz.senslog.connector.model.v1.Observation;
+import cz.senslog.connector.model.v1.Record;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.*;
+import java.util.function.Function;
+
+public class AlapansModelSenslogV1ModelConverter implements Converter<AlapansModel, SenslogV1Model> {
+
+    private static final Logger logger = LogManager.getLogger(AlapansModelSenslogV1ModelConverter.class);
+
+    private static final Map<String, Long> DEVICE_ID_TO_UNIT_ID;
+
+    private static final Function<UnitData, List<Observation>> SENSOR_MAPPING_FNC;
+    private static final int NM_OF_SENSORS;
+
+    static {
+        DEVICE_ID_TO_UNIT_ID = new HashMap<>();
+
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe05335e", 428059001L);
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe053381", 428059002L);
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe05335c", 428059003L);
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe053359", 428059004L);
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe05335f", 428059200L);
+        DEVICE_ID_TO_UNIT_ID.put("eui-ac1f09fffe053363", 428059005L);
+
+        NM_OF_SENSORS = 6;
+        SENSOR_MAPPING_FNC = (u) -> Arrays.asList(
+                new Observation(){{setSensorId(340020000L); setValue(u.getAirTemperature());    }},
+                new Observation(){{setSensorId(410010000L); setValue(u.getAirHumidity());       }},
+                new Observation(){{setSensorId(340031000L); setValue(u.getSoilTemperature1());  }},
+                new Observation(){{setSensorId(340032000L); setValue(u.getSoilTemperature2());  }},
+                new Observation(){{setSensorId(410021000L); setValue(u.getSoilMoisture1());     }},
+                new Observation(){{setSensorId(410022000L); setValue(u.getSoilMoisture2());     }},
+                new Observation(){{setSensorId(360010000L); setValue(u.getBatteryVoltage());    }}
+        );
+    }
+
+
+    @Override
+    public SenslogV1Model convert(AlapansModel inputModel) {
+        if (inputModel == null) { return null; }
+        List<Record> observations = new ArrayList<>(inputModel.getUnitData().size() * NM_OF_SENSORS);
+        for (UnitData unitData : inputModel.getUnitData()) {
+            for (Observation observation : SENSOR_MAPPING_FNC.apply(unitData)) {
+                if (observation.getValue() == null) { continue; }
+                Long unitId = DEVICE_ID_TO_UNIT_ID.get(unitData.getDeviceId());
+                if (unitId == null) {
+                    logger.warn("The device ID {} is not registered.", unitData.getDeviceId()); continue;
+                }
+                observation.setUnitId(unitId);
+                observation.setTime(unitData.getTimestamp());
+                observations.add(observation);
+            }
+        }
+        return new SenslogV1Model(observations, inputModel.getFrom(), inputModel.getTo());
+    }
+}

+ 1 - 3
connector-model/src/main/java/cz/senslog/connector/model/converter/AzureModelSenslogV1ModelConverter.java

@@ -87,9 +87,7 @@ public final class AzureModelSenslogV1ModelConverter implements Converter<AzureM
 
         logger.debug("Creating a new SenslogV1 model.");
         SenslogV1Model outputModel = new SenslogV1Model(observations, inputModel.getFrom(), inputModel.getTo());
-
-        logger.info("Conversion was completed successfully.");
-
+        logger.debug("Conversion was completed successfully.");
         return outputModel;
     }
 }

+ 1 - 3
connector-model/src/main/java/cz/senslog/connector/model/converter/AzureModelSenslogV2ModelConverter.java

@@ -74,9 +74,7 @@ public class AzureModelSenslogV2ModelConverter implements Converter<AzureModel,
 
         logger.debug("Creating a new SenslogV2 model.");
         SenslogV2Model outputModel = new SenslogV2Model(observations, inputModel.getFrom(), inputModel.getTo());;
-
-        logger.info("Conversion was completed successfully.");
-
+        logger.debug("Conversion was completed successfully.");
         return outputModel;
     }
 }

+ 2 - 2
connector-model/src/main/java/cz/senslog/connector/model/converter/DrutesModelSenslogV1ModelConverter.java

@@ -95,7 +95,7 @@ public class DrutesModelSenslogV1ModelConverter implements Converter<DrutesModel
         List<Record> observations = new LinkedList<>();
 
         for (PredictionData prediction : model.getPredictions()) {
-            logger.info("Converting sensor with ID {}.", prediction.getSensorId());
+            logger.debug("Converting sensor with ID {}.", prediction.getSensorId());
 
             int dayOffset = prediction.getPredictCreated().getDayOfWeek().getValue();
             OffsetDateTime timestamp = prediction.getPredictFrom().plusSeconds(prediction.getTimeshift());
@@ -108,7 +108,7 @@ public class DrutesModelSenslogV1ModelConverter implements Converter<DrutesModel
 
         logger.debug("Creating a new SenslogV1 model.");
         SenslogV1Model senslogV1Model = new SenslogV1Model(observations, model.getFrom(), model.getTo());
-        logger.info("Conversion was completed successfully.");
+        logger.debug("Conversion was completed successfully.");
         return senslogV1Model;
     }
 }

+ 1 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java

@@ -25,5 +25,6 @@ public class ModelConverterProvider extends ConverterProvider {
         register(FieldClimateModelSenslogV1ModelConverter.class);
         register(SenslogV1ModelAFarCloudModelConverter.class);
         register(DrutesModelSenslogV1ModelConverter.class);
+        register(AlapansModelSenslogV1ModelConverter.class);
     }
 }

+ 12 - 0
connector-model/src/main/java/cz/senslog/connector/model/demo/DemoModel.java

@@ -0,0 +1,12 @@
+package cz.senslog.connector.model.demo;
+
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+
+public class DemoModel extends AbstractModel {
+
+    protected DemoModel(OffsetDateTime from, OffsetDateTime to) {
+        super(from, to);
+    }
+}

+ 3 - 5
connector-model/src/main/java/cz/senslog/connector/model/v1/Observation.java

@@ -3,8 +3,6 @@
 
 package cz.senslog.connector.model.v1;
 
-import static cz.senslog.connector.tools.json.BasicJson.objectToJson;
-
 /**
  * The class {@code Observation} represents a record for Senslog V1.
  *
@@ -36,8 +34,8 @@ public class Observation extends Record {
         this.value = value;
     }
 
-    @Override
-    public String toString() {
-        return objectToJson(this);
+    public void setValue(Double value) {
+        setValue(value != null ? value.floatValue() : null);
     }
+
 }

+ 1 - 3
connector-model/src/test/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverterTest.java

@@ -2,9 +2,9 @@ package cz.senslog.connector.model.converter;
 
 import cz.senslog.connector.model.afarcloud.AFarCloudModel;
 import cz.senslog.connector.model.v1.*;
+import cz.senslog.connector.model.v1.Record;
 import org.junit.jupiter.api.Test;
 
-import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
@@ -12,8 +12,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static cz.senslog.connector.tools.json.BasicJson.objectToJson;
-import static org.junit.jupiter.api.Assertions.*;
 
 class SenslogV1ModelAFarCloudModelConverterTest {
 

+ 1 - 2
connector-push-afarcloud/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-push-afarcloud</artifactId>
     <name>push-afarcloud</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 1 - 7
connector-push-api/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,17 +11,11 @@
 
     <artifactId>connector-push-api</artifactId>
     <name>push-api</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
             <groupId>cz.senslog</groupId>
-            <artifactId>connector-tools</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>cz.senslog</groupId>
             <artifactId>connector-model</artifactId>
             <version>${project.parent.version}</version>
         </dependency>

+ 1 - 2
connector-push-rest-senslog-v1/pom.xml → connector-push-senslog-v1/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,7 +11,6 @@
 
     <artifactId>connector-push-senslog-v1</artifactId>
     <name>push-senslog-v1</name>
-    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
 
     <dependencies>

+ 0 - 0
connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Config.java → connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Config.java


+ 38 - 38
connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProvider.java → connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProvider.java

@@ -1,38 +1,38 @@
-package cz.senslog.connector.push.rest.senslog.v1;
-
-import cz.senslog.connector.model.config.DefaultConfig;
-import cz.senslog.connector.model.v1.SenslogV1Model;
-import cz.senslog.connector.push.api.ConnectorPushProvider;
-import cz.senslog.connector.push.api.ConnectorPusher;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
-
-/**
- * The class {@code SenslogV1ConnectorPushProvider} represents a concrete implementation of {@link ConnectorPushProvider}.
- * Contains basic functionality to configure implementation of {@link ConnectorPusher} from the default configuration.
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-public final class SenslogV1ConnectorPushProvider implements ConnectorPushProvider {
-
-    private static final Logger logger = LogManager.getLogger(SenslogV1ConnectorPushProvider.class);
-
-    @Override
-    public ConnectorPusher<SenslogV1Model> createPusher(DefaultConfig config) {
-        logger.info("Initialization a new push provider {}.", SenslogV1ConnectorPushProvider.class);
-
-        logger.debug("Creating a new configuration.");
-        SenslogV1Config senslogConfig = new SenslogV1Config(config);
-        logger.info("Configuration for {} was created successfully.", SenslogV1Pusher.class);
-
-        logger.debug("Creating a new instance of {}.", SenslogV1Pusher.class);
-        SenslogV1Pusher pusher = new SenslogV1Pusher(senslogConfig, newHttpClient());
-        logger.info("Pusher for {} was created successfully.", SenslogV1Pusher.class);
-
-        return pusher;
-    }
-}
+package cz.senslog.connector.push.rest.senslog.v1;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import cz.senslog.connector.push.api.ConnectorPushProvider;
+import cz.senslog.connector.push.api.ConnectorPusher;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
+
+/**
+ * The class {@code SenslogV1ConnectorPushProvider} represents a concrete implementation of {@link ConnectorPushProvider}.
+ * Contains basic functionality to configure implementation of {@link ConnectorPusher} from the default configuration.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+public final class SenslogV1ConnectorPushProvider implements ConnectorPushProvider {
+
+    private static final Logger logger = LogManager.getLogger(SenslogV1ConnectorPushProvider.class);
+
+    @Override
+    public ConnectorPusher<SenslogV1Model> createPusher(DefaultConfig config) {
+        logger.info("Initialization a new push provider {}.", SenslogV1ConnectorPushProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        SenslogV1Config senslogConfig = new SenslogV1Config(config);
+        logger.info("Configuration for {} was created successfully.", SenslogV1Pusher.class);
+
+        logger.debug("Creating a new instance of {}.", SenslogV1Pusher.class);
+        SenslogV1Pusher pusher = new SenslogV1Pusher(senslogConfig, newHttpClient());
+        logger.info("Pusher for {} was created successfully.", SenslogV1Pusher.class);
+
+        return pusher;
+    }
+}

+ 263 - 265
connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Pusher.java → connector-push-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Pusher.java

@@ -1,266 +1,264 @@
-package cz.senslog.connector.push.rest.senslog.v1;
-
-import com.google.gson.reflect.TypeToken;
-import cz.senslog.connector.model.config.HostConfig;
-import cz.senslog.connector.tools.http.*;
-import cz.senslog.connector.model.v1.Observation;
-import cz.senslog.connector.model.v1.Position;
-import cz.senslog.connector.model.v1.Record;
-import cz.senslog.connector.model.v1.SenslogV1Model;
-import cz.senslog.connector.push.api.ConnectorPusher;
-import cz.senslog.connector.tools.util.Tuple;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.lang.reflect.Type;
-import java.util.*;
-import java.util.concurrent.LinkedTransferQueue;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
-
-import static cz.senslog.connector.tools.http.HttpContentType.TEXT_PLAIN;
-import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
-import static java.util.Collections.emptyList;
-
-/**
- * The class {@code SenslogV1Pusher} represents an implementation of {@link ConnectorPusher}.
- * The class receive a {@link SenslogV1Model} which contains data to send.
- *
- * <h2>Initialization</h2>
- *  - nothing to initialize
- *
- * <h2>Push</h2>
- * If the model received does not exist or does not contain any observation, nothing will be sent.
- * For each observation is created a request which is send according to configuration.
- * The observation that can not be sent (response contains error) is moved to be sent later.
- * If more than the number of fails requests fail, the sending will over.
- *
- * @author Lukas Cerny
- * @version 1.0
- * @since 1.0
- */
-class  SenslogV1Pusher implements ConnectorPusher<SenslogV1Model> {
-
-    private static final Logger logger = LogManager.getLogger(SenslogV1Pusher.class);
-
-    private static final int MAX_AUTH_ERRORS = 2;
-
-    /** Configuration. */
-    private final SenslogV1Config config;
-
-    /** Http client for requests. */
-    private final HttpClient httpClient;
-
-    /** Queue of observations wait to be send. */
-    private final Queue<Record> observationQueue;
-
-    /** List of failed observations. */
-    private final List<Record> failedObservations;
-
-    private Tuple<Boolean, HttpCookie> authCookie;
-    private final Map<Long, AtomicInteger> authError;
-
-    /**
-     * Constructor of the class sets all attributes.
-     * @param config - configuration for pusher.
-     * @param httpClient - http client.
-     */
-    SenslogV1Pusher(SenslogV1Config config, HttpClient httpClient) {
-        this.config = config;
-        this.httpClient = httpClient;
-        this.observationQueue = new LinkedTransferQueue<>();
-        this.failedObservations = new ArrayList<>();
-        this.authCookie = Tuple.of(false, null);
-        this.authError = new HashMap<>();
-    }
-
-    @Override
-    public void init() {}
-
-    private synchronized HttpCookie getAuthCookie() {
-        if (authCookie.getItem1()) {
-            return authCookie.getItem2();
-        }
-
-        if (config.getAuth() == null) {
-            authCookie = Tuple.of(true, null);
-            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 auth cookie from the server: {}.", config.getBaseUrl());
-
-        HttpResponse response = httpClient.send(request);
-        logger.info("Received new auth token with the status '{}' from the server {}.", response.getStatus(), config.getBaseUrl());
-
-        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 void push(SenslogV1Model model) {
-
-        if (model != null && model.getObservations() != null) {
-
-            List<Record> observations = model.getObservations();
-            logger.info("Received {} new observations from {} to {} to push.",
-                    observations.size(), model.getFrom(), model.getTo());
-
-            logger.debug("Adding all new observations to the queue.");
-            observationQueue.addAll(observations);
-        }
-
-        if (!failedObservations.isEmpty()) {
-            logger.info("Adding {} failed observations to the queue.", failedObservations.size());
-            observationQueue.addAll(failedObservations);
-            failedObservations.clear();
-        }
-
-        int totalToPush = observationQueue.size();
-        int pushedSuccessfully = 0;
-        while (!observationQueue.isEmpty()) {
-            Record record = observationQueue.remove();
-
-            String id = UUID.randomUUID().toString();
-
-            logger.debug("Creating a request for the observation {}.", id);
-            logger.info("Observation {} {}", id, record);
-
-            if (!validateRequiredValues(
-                    record::getUnitId,
-                    record::getTime)
-            ) {
-                logger.error("Observation {} can not be send because required values are null.", id); continue;
-            }
-
-            HttpRequest.Builder reqBuilder = null; // TODO refactor
-            if (record instanceof Observation) {
-                reqBuilder = prepareRequest((Observation) record, config.getBaseUrl());
-            } else if (record instanceof Position) {
-                reqBuilder = prepareRequest((Position) record, config.getBaseUrl());
-            }
-
-            if (reqBuilder == null) {
-                logger.error("Request for the {} was not created.", id); continue;
-            } else {
-                logger.debug("Request for the {} was created successfully.", id);
-            }
-
-            String responseBody = sendRequest(reqBuilder, id);
-            logger.debug("Parsing body of the response.");
-            boolean result = Boolean.parseBoolean(responseBody);
-
-            if (result) {
-                pushedSuccessfully += 1;
-            } else {
-                logger.warn("Observation {} was rejected.", id);
-            }
-        }
-        logger.info("Total pushed successfully {}/{} observations.", pushedSuccessfully, totalToPush);
-    }
-
-    private String sendRequest(HttpRequest.Builder reqBuilder, String reqId) {
-        HttpCookie authCookie = getAuthCookie();
-
-        HttpRequest request;
-        if (authCookie == null) {
-            request = reqBuilder.build();
-        } else if (!authCookie.isSecure()) {
-            logger.error("Auth cookie is not valid to be used."); return "false";
-        } else {
-            request = reqBuilder.addCookie(authCookie).build();
-        }
-
-        logger.info("Sending request for the {}", reqId);
-        HttpResponse response = httpClient.send(request);
-        logger.info("Response of {} {}", reqId, response);
-
-        long threadId = Thread.currentThread().getId();
-        AtomicInteger authErrors = authError.computeIfAbsent(threadId, id -> new AtomicInteger(0));
-        if (response.getStatus() == HttpCode.UNAUTHORIZED && authErrors.get() <= MAX_AUTH_ERRORS) {
-            this.authCookie = Tuple.of(false, null);
-            authErrors.incrementAndGet();
-            return sendRequest(reqBuilder, reqId);
-        }
-
-        String responseBody = response.getBody();
-        if (response.isError()) {
-            logger.error("Observation {} was not send. Reason {}.", reqId, responseBody);
-        }
-
-        return responseBody;
-    }
-
-    private static HttpRequest.Builder prepareRequest(Observation observation, String hostUrl) {
-        return HttpRequest.newBuilder()
-                .contentType(TEXT_PLAIN)
-                .url(URLBuilder.newBuilder(hostUrl, "FeederServlet")
-                        .addParam("Operation", "InsertObservation")
-                        .addParam("value", observation.getValue())
-                        .addParam("date", observation.getFormattedTime())
-                        .addParam("unit_id", observation.getUnitId())
-                        .addParam("sensor_id", observation.getSensorId())
-                        .build())
-                .GET();
-    }
-
-    private static HttpRequest.Builder prepareRequest(Position position, String hostUrl) {
-
-        if (!validateRequiredValues(
-                position::getLatitude,
-                position::getLongitude
-        )) {
-            return null;
-        }
-
-        return HttpRequest.newBuilder()
-                .contentType(TEXT_PLAIN)
-                .url(URLBuilder.newBuilder(hostUrl, "FeederServlet")
-                        .addParam("Operation", "InsertPosition")
-                        .addParam("date", position.getFormattedTime())
-                        .addParam("unit_id", position.getUnitId())
-                        .addParam("lat", position.getLatitude())
-                        .addParam("lon", position.getLongitude())
-                        .addParam("alt", position.getAltitude())
-                        .addParam("speed", position.getSpeed())
-                        .addParam("dop", position.getDilutionOfPrecision())
-                        .build())
-                .GET();
-    }
-
-    @SafeVarargs
-    private static boolean validateRequiredValues(Supplier<Object>... attributes) {
-        for (Supplier<Object> attribute : attributes) {
-            if (attribute.get() == null) {
-                return false;
-            }
-        }
-        return true;
-    }
+package cz.senslog.connector.push.rest.senslog.v1;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.connector.tools.http.*;
+import cz.senslog.connector.model.v1.Observation;
+import cz.senslog.connector.model.v1.Position;
+import cz.senslog.connector.model.v1.Record;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import cz.senslog.connector.push.api.ConnectorPusher;
+import cz.senslog.connector.tools.util.Tuple;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import static cz.senslog.connector.tools.http.HttpContentType.TEXT_PLAIN;
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+import static java.util.Collections.emptyList;
+
+/**
+ * The class {@code SenslogV1Pusher} represents an implementation of {@link ConnectorPusher}.
+ * The class receive a {@link SenslogV1Model} which contains data to send.
+ *
+ * <h2>Initialization</h2>
+ *  - nothing to initialize
+ *
+ * <h2>Push</h2>
+ * If the model received does not exist or does not contain any observation, nothing will be sent.
+ * For each observation is created a request which is send according to configuration.
+ * The observation that can not be sent (response contains error) is moved to be sent later.
+ * If more than the number of fails requests fail, the sending will over.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.0
+ */
+class  SenslogV1Pusher implements ConnectorPusher<SenslogV1Model> {
+
+    private static final Logger logger = LogManager.getLogger(SenslogV1Pusher.class);
+
+    private static final int MAX_AUTH_ERRORS = 2;
+
+    /** Configuration. */
+    private final SenslogV1Config config;
+
+    /** Http client for requests. */
+    private final HttpClient httpClient;
+
+    /** Queue of observations wait to be send. */
+    private final Queue<Record> observationQueue;
+
+    /** List of failed observations. */
+    private final List<Record> failedObservations;
+
+    private Tuple<Boolean, HttpCookie> authCookie;
+    private final Map<Long, AtomicInteger> authError;
+
+    /**
+     * Constructor of the class sets all attributes.
+     * @param config - configuration for pusher.
+     * @param httpClient - http client.
+     */
+    SenslogV1Pusher(SenslogV1Config config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+        this.observationQueue = new LinkedTransferQueue<>();
+        this.failedObservations = new ArrayList<>();
+        this.authCookie = Tuple.of(false, null);
+        this.authError = new HashMap<>();
+    }
+
+    @Override
+    public void init() {}
+
+    private synchronized HttpCookie getAuthCookie() {
+        if (authCookie.getItem1()) {
+            return authCookie.getItem2();
+        }
+
+        if (config.getAuth() == null) {
+            authCookie = Tuple.of(true, null);
+            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 auth cookie from the server: {}.", config.getBaseUrl());
+
+        HttpResponse response = httpClient.send(request);
+        logger.info("Received new auth token with the status '{}' from the server {}.", response.getStatus(), config.getBaseUrl());
+
+        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 void push(SenslogV1Model model) {
+        if (model == null) { return; }
+
+        if (model.getObservations() != null) {
+
+            List<Record> observations = model.getObservations();
+            logger.info("Received {} new observations from {} to {} to push.",
+                    observations.size(), model.getFrom(), model.getTo());
+
+            logger.debug("Adding all new observations to the queue.");
+            observationQueue.addAll(observations);
+        }
+
+        if (!failedObservations.isEmpty()) {
+            logger.info("Adding {} failed observations to the queue.", failedObservations.size());
+            observationQueue.addAll(failedObservations);
+            failedObservations.clear();
+        }
+
+        int totalToPush = observationQueue.size();
+        int pushedSuccessfully = 0;
+        while (!observationQueue.isEmpty()) {
+            Record record = observationQueue.remove();
+
+            String id = UUID.randomUUID().toString();
+            logger.debug("Observation {} {}", id, record);
+
+            if (!validateRequiredValues(
+                    record::getUnitId,
+                    record::getTime)
+            ) {
+                logger.error("Observation {} can not be send because required values are null.", record); continue;
+            }
+
+            HttpRequest.Builder reqBuilder = null; // TODO refactor
+            if (record instanceof Observation) {
+                reqBuilder = prepareRequest((Observation) record, config.getBaseUrl());
+            } else if (record instanceof Position) {
+                reqBuilder = prepareRequest((Position) record, config.getBaseUrl());
+            }
+
+            if (reqBuilder == null) {
+                logger.error("Request for the {} was not created.", id); continue;
+            } else {
+                logger.debug("Request for the {} was created successfully.", id);
+            }
+
+            String responseBody = sendRequest(reqBuilder, id);
+            logger.debug("Parsing body of the response.");
+            boolean result = Boolean.parseBoolean(responseBody);
+
+            if (result) {
+                pushedSuccessfully += 1;
+            } else {
+                logger.warn("Observation {} was rejected.", record);
+            }
+        }
+        logger.info("Total pushed successfully {}/{} observations.", pushedSuccessfully, totalToPush);
+    }
+
+    private String sendRequest(HttpRequest.Builder reqBuilder, String reqId) {
+        HttpCookie authCookie = getAuthCookie();
+
+        HttpRequest request;
+        if (authCookie == null) {
+            request = reqBuilder.build();
+        } else if (!authCookie.isSecure()) {
+            logger.error("Auth cookie is not valid to be used."); return "false";
+        } else {
+            request = reqBuilder.addCookie(authCookie).build();
+        }
+
+        logger.debug("Sending request for the {}", reqId);
+        HttpResponse response = httpClient.send(request);
+        logger.debug("Response of {} {}", reqId, response);
+
+        long threadId = Thread.currentThread().getId();
+        AtomicInteger authErrors = authError.computeIfAbsent(threadId, id -> new AtomicInteger(0));
+        if (response.getStatus() == HttpCode.UNAUTHORIZED && authErrors.get() <= MAX_AUTH_ERRORS) {
+            this.authCookie = Tuple.of(false, null);
+            authErrors.incrementAndGet();
+            return sendRequest(reqBuilder, reqId);
+        }
+
+        String responseBody = response.getBody();
+        if (response.isError()) {
+            logger.error("Observation {} was not send. Reason {}.", reqId, responseBody);
+        }
+
+        return responseBody;
+    }
+
+    private static HttpRequest.Builder prepareRequest(Observation observation, String hostUrl) {
+        return HttpRequest.newBuilder()
+                .contentType(TEXT_PLAIN)
+                .url(URLBuilder.newBuilder(hostUrl, "FeederServlet")
+                        .addParam("Operation", "InsertObservation")
+                        .addParam("value", observation.getValue())
+                        .addParam("date", observation.getFormattedTime())
+                        .addParam("unit_id", observation.getUnitId())
+                        .addParam("sensor_id", observation.getSensorId())
+                        .build())
+                .GET();
+    }
+
+    private static HttpRequest.Builder prepareRequest(Position position, String hostUrl) {
+
+        if (!validateRequiredValues(
+                position::getLatitude,
+                position::getLongitude
+        )) {
+            return null;
+        }
+
+        return HttpRequest.newBuilder()
+                .contentType(TEXT_PLAIN)
+                .url(URLBuilder.newBuilder(hostUrl, "FeederServlet")
+                        .addParam("Operation", "InsertPosition")
+                        .addParam("date", position.getFormattedTime())
+                        .addParam("unit_id", position.getUnitId())
+                        .addParam("lat", position.getLatitude())
+                        .addParam("lon", position.getLongitude())
+                        .addParam("alt", position.getAltitude())
+                        .addParam("speed", position.getSpeed())
+                        .addParam("dop", position.getDilutionOfPrecision())
+                        .build())
+                .GET();
+    }
+
+    @SafeVarargs
+    private static boolean validateRequiredValues(Supplier<Object>... attributes) {
+        for (Supplier<Object> attribute : attributes) {
+            if (attribute.get() == null) {
+                return false;
+            }
+        }
+        return true;
+    }
 }

+ 0 - 0
connector-push-rest-senslog-v1/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider → connector-push-senslog-v1/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider


+ 0 - 0
connector-push-rest-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConfigTest.java → connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConfigTest.java


+ 0 - 0
connector-push-rest-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProviderTest.java → connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProviderTest.java


+ 0 - 0
connector-push-rest-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1PusherTest.java → connector-push-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1PusherTest.java


+ 34 - 16
connector-tools/pom.xml

@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>connectors</artifactId>
+        <artifactId>connector-period</artifactId>
         <groupId>cz.senslog</groupId>
         <version>1.0-SNAPSHOT</version>
     </parent>
@@ -11,39 +11,48 @@
 
     <artifactId>connector-tools</artifactId>
     <name>tools</name>
-    <version>${project.parent.version}</version>
 
     <dependencies>
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>2.8.5</version>
+            <version>2.10.1</version>
         </dependency>
+
         <dependency>
-            <groupId>org.apache.httpcomponents</groupId>
-            <artifactId>httpclient</artifactId>
-            <version>4.5.9</version>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+            <version>5.2.1</version>
         </dependency>
+
         <dependency>
             <groupId>org.yaml</groupId>
             <artifactId>snakeyaml</artifactId>
-            <version>1.24</version>
+            <version>2.0</version>
         </dependency>
+
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-api</artifactId>
-            <version>2.12.0</version>
+            <version>2.20.0</version>
         </dependency>
+
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-core</artifactId>
-            <version>2.12.0</version>
+            <version>2.20.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>2.20.0</version>
         </dependency>
 
         <dependency>
-            <groupId>com.github.everit-org.json-schema</groupId>
-            <artifactId>org.everit.json.schema</artifactId>
-            <version>1.11.1</version>
+            <groupId>com.github.erosb</groupId>
+            <artifactId>everit-json-schema</artifactId>
+            <version>1.14.2</version>
         </dependency>
 
         <dependency>
@@ -54,16 +63,25 @@
 
         <dependency>
             <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.9.2</version>
+<!--            <scope>test</scope>-->
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
-            <version>5.4.2</version>
-            <scope>test</scope>
+            <version>5.9.2</version>
+<!--            <scope>test</scope>-->
         </dependency>
+
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
-            <version>3.0.0</version>
-            <scope>test</scope>
+            <version>4.11.0</version>
+<!--            <scope>test</scope>-->
         </dependency>
+
     </dependencies>
 
     <repositories>

+ 106 - 47
connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpClient.java

@@ -1,36 +1,56 @@
 package cz.senslog.connector.tools.http;
 
 import cz.senslog.connector.tools.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 org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.CookieStore;
+import org.apache.hc.client5.http.cookie.StandardCookieSpec;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpMessage;
+import org.apache.hc.core5.http.io.SocketConfig;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.ssl.TLS;
+import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
+import org.apache.hc.core5.pool.PoolReusePolicy;
+import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.Socket;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.KeyManagementException;
-import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Map;
 
-import static org.apache.http.HttpHeaders.*;
+import static org.apache.hc.core5.http.HttpHeaders.*;
+
 
 /**
- * The class {@code HttpClient} represents a wrapper for {@link org.apache.http.client.HttpClient}.
+ * The class {@code HttpClient} represents a wrapper for {@link org.apache.hc.client5.http.classic.HttpClient}.
  * Provides functionality of sending GET and POST request. Otherwise is returned response with {@see #BAD_REQUEST}.
  *
  * @author Lukas Cerny
@@ -40,40 +60,91 @@ import static org.apache.http.HttpHeaders.*;
 public class HttpClient {
 
     /** Instance of http client. */
-    private final org.apache.http.client.HttpClient client;
-    private final BasicCookieStore cookieStore;
+    private final org.apache.hc.client5.http.classic.HttpClient client;
+    private final CookieStore cookieStore;
+
+    static {
+//        System.setProperty("javax.net.ssl.trustStore", "/home/vrenclouff/Downloads/temp/clienttruststore.jks");
+//        System.setProperty("javax.net.ssl.keyStorePassword", "password");
+    }
+
+    private static HttpClient newClient() {
+
+        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
+//                    .setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create()
+//                            .setSslContext(SSLContexts.createSystemDefault())
+////                            .setSslContext(sslContext)
+//                            .setTlsVersions(TLS.V_1_3)
+//                            .build())
+                .setDefaultSocketConfig(SocketConfig.custom()
+                        .setSoTimeout(Timeout.ofMinutes(1))
+                        .build())
+                .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
+                .setConnPoolPolicy(PoolReusePolicy.LIFO)
+                .setDefaultConnectionConfig(ConnectionConfig.custom()
+                        .setSocketTimeout(Timeout.ofMinutes(1))
+                        .setConnectTimeout(Timeout.ofMinutes(1))
+                        .setTimeToLive(TimeValue.ofMinutes(10))
+                        .build())
+                .build();
 
+        CookieStore cookieStore = new BasicCookieStore();
+        CloseableHttpClient client = HttpClients.custom()
+                .setConnectionManager(connectionManager)
+                .setDefaultCookieStore(cookieStore)
+                .setDefaultRequestConfig(RequestConfig.custom()
+                        .setCookieSpec(StandardCookieSpec.STRICT)
+                        .build())
+                .build();
+
+        return new HttpClient(client, 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());
+//        return new HttpClient(HttpClientBuilder.create(), new BasicCookieStore());
+        return newClient();
     }
 
     public static HttpClient newHttpSSLClient() {
+        return newClient();
+        /*
         try {
+
             SSLContextBuilder builder = new SSLContextBuilder();
-            builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
+            builder.loadTrustMaterial(null, (chain, authType) -> true);
+
 
             SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(builder.build(),
                     SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
             return new HttpClient(HttpClientBuilder.create().setSSLSocketFactory(sslSF), new BasicCookieStore());
 
         } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
             return null;
+        } catch (UnrecoverableKeyException | NoSuchProviderException e) {
+            throw new RuntimeException(e);
         }
+
+         */
     }
 
     /**
      * Private constructors sets http client.
      */
-    private HttpClient(HttpClientBuilder httpClientBuilder, BasicCookieStore cookieStore) {
+    private HttpClient(HttpClientBuilder httpClientBuilder, CookieStore cookieStore) {
         this.cookieStore = cookieStore;
         this.client = httpClientBuilder.setDefaultCookieStore(cookieStore).build();
     }
 
+    private HttpClient(org.apache.hc.client5.http.classic.HttpClient httpClient, CookieStore cookieStore) {
+        this.client = httpClient;
+        this.cookieStore = cookieStore;
+    }
+
     /**
      * Sends http request.
      * @param request - virtual request.
@@ -114,20 +185,14 @@ public class HttpClient {
 
         cookieStore.clear();
         for (HttpCookie cookie : request.getCookies()) {
-            cookieStore.addCookie(cookie);
+            cookieStore.addCookie(cookie.get());
         }
 
-        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;
+        return client.execute(requestGet, res -> HttpResponse.newBuilder()
+                .status(res.getCode())
+                .headers(getHeaders(res))
+                .body(getBody(res.getEntity()))
+                .build());
     }
 
     /**
@@ -149,17 +214,11 @@ public class HttpClient {
 
         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;
+        return client.execute(requestPost, res -> HttpResponse.newBuilder()
+                .status(res.getCode())
+                .headers(getHeaders(res))
+                .body(getBody(res.getEntity()))
+                .build());
     }
 
     /**
@@ -167,7 +226,7 @@ public class HttpClient {
      * @param userRequest - virtual request.
      * @param httpRequest - real request prepared to send.
      */
-    private void setBasicHeaders(HttpRequest userRequest, HttpRequestBase httpRequest) {
+    private void setBasicHeaders(HttpRequest userRequest, HttpUriRequestBase httpRequest) {
 
         httpRequest.setHeader(USER_AGENT, "SenslogConnector/1.0");
         httpRequest.setHeader(CACHE_CONTROL, "no-cache");
@@ -184,7 +243,7 @@ public class HttpClient {
      */
     private Map<String, String> getHeaders(HttpMessage response) {
         Map<String, String> headers = new HashMap<>();
-        for (Header header : response.getAllHeaders()) {
+        for (Header header : response.getHeaders()) {
             headers.put(header.getName(), header.getValue());
         }
         return headers;

+ 22 - 15
connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpCookie.java

@@ -1,39 +1,46 @@
 package cz.senslog.connector.tools.http;
 
-import org.apache.http.impl.cookie.BasicClientCookie;
 
-public class HttpCookie extends BasicClientCookie {
+import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
+
+public class HttpCookie {
+
+    private final BasicClientCookie cookie;
 
     public static HttpCookie empty() {
         HttpCookie cookie = new HttpCookie("", "", "", "");
-        cookie.setSecure(false);
+        cookie.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);
+        this.cookie = new BasicClientCookie(name, value);
+        this.cookie.setDomain(domain);
+        this.cookie.setPath(path);
+        this.cookie.setSecure(true);
     }
 
-    @Override
     public String getName() {
-        return super.getName();
+        return this.cookie.getName();
     }
 
-    @Override
     public String getValue() {
-        return super.getValue();
+        return this.cookie.getValue();
     }
 
-    @Override
     public String getDomain() {
-        return super.getDomain();
+        return this.cookie.getDomain();
     }
 
-    @Override
     public String getPath() {
-        return super.getPath();
+        return this.cookie.getPath();
+    }
+
+    public boolean isSecure() {
+        return this.cookie.isSecure();
+    }
+
+    public BasicClientCookie get() {
+        return this.cookie;
     }
 }

+ 2 - 1
connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpHeader.java

@@ -1,6 +1,7 @@
 package cz.senslog.connector.tools.http;
 
-import org.apache.http.HttpHeaders;
+
+import org.apache.hc.core5.http.HttpHeaders;
 
 public final class HttpHeader {
     public static final String AUTHORIZATION = HttpHeaders.AUTHORIZATION;

+ 0 - 5
connector-tools/src/main/java/cz/senslog/connector/tools/http/HttpRequest.java

@@ -102,9 +102,4 @@ public class HttpRequest {
     public HttpCookie[] getCookies() {
         return cookies;
     }
-
-    @Override
-    public String toString() {
-        return objectToJson(this);
-    }
 }

+ 35 - 3
connector-tools/src/main/java/cz/senslog/connector/tools/json/BasicJson.java

@@ -9,14 +9,16 @@ import cz.senslog.connector.tools.util.Tuple;
 
 import java.io.IOException;
 import java.io.StringReader;
+import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.OffsetDateTime;
 import java.time.ZonedDateTime;
+import java.util.Optional;
 
 import static com.google.gson.stream.JsonToken.END_DOCUMENT;
-import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
-import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+import static java.time.format.DateTimeFormatter.*;
 
 /**
  * The class {@code BasicJson} represents a basic wrapper for {@link Gson} library.
@@ -37,11 +39,13 @@ import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 public class BasicJson {
 
     /** Instance of json converter. */
-    private static Gson gson = new GsonBuilder()
+    private static final Gson gson = new GsonBuilder()
             .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+            .registerTypeAdapter(LocalTime.class, new LocalTimeAdapter())
             .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter())
             .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter())
             .registerTypeAdapter(Class.class, new ClassAdapter())
+            .registerTypeAdapter(Optional.class, new OptionalAdapter())
             .create();
 
     /** Formatter for {@see LocalDateTime}. */
@@ -58,6 +62,20 @@ public class BasicJson {
         }
     }
 
+    /** Formatter for {@see LocalDateTime}. */
+    private static class LocalTimeAdapter implements JsonSerializer<LocalTime>, JsonDeserializer<LocalTime> {
+
+        @Override
+        public JsonElement serialize(LocalTime localTime, Type type, JsonSerializationContext jsonSerializationContext) {
+            return new JsonPrimitive(localTime.format(ISO_TIME));
+        }
+
+        @Override
+        public LocalTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+            return LocalTime.parse(jsonElement.getAsString(), ISO_TIME);
+        }
+    }
+
     /** Formatter for {@see ZonedDateTime}. */
     private static class ZonedDateTimeAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
 
@@ -104,6 +122,20 @@ public class BasicJson {
         }
     }
 
+    private static class OptionalAdapter implements JsonSerializer<Optional<?>>, JsonDeserializer<Optional<?>> {
+
+        @Override
+        public JsonElement serialize(Optional<?> optional, Type type, JsonSerializationContext jsonSerializationContext) {
+            return new JsonPrimitive(optional.isPresent() ? optional.get().toString() : "null");
+        }
+
+        @Override
+        public Optional<?> deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+            Type actualType = ((ParameterizedType) type).getActualTypeArguments()[0];
+            return jsonElement.getAsString().equals("null") ? Optional.empty() : Optional.ofNullable(jsonToObject(jsonElement.getAsString(), actualType));
+        }
+    }
+
     /**
      * Deserialize json to a typed object according to class.
      * @param jsonString - json string.

+ 86 - 0
connector-tools/src/main/java/cz/senslog/connector/tools/senslog/auth/SensLogAuthService.java

@@ -0,0 +1,86 @@
+package cz.senslog.connector.tools.senslog.auth;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.connector.tools.http.*;
+import cz.senslog.connector.tools.util.Tuple;
+
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+
+public final class SensLogAuthService {
+
+    private static final Map<String, SensLogAuthService> INSTANCES;
+
+    private final Map<Tuple<String, String>, HttpCookie> authCookies;
+
+    private final String sensLogUrl;
+
+    private final URI baseUri;
+
+    private final HttpClient httpClient;
+
+    static {
+        INSTANCES = new HashMap<>();
+    }
+
+    public static SensLogAuthService getInstance(String sensLogUrl) {
+        if (INSTANCES.containsKey(sensLogUrl)) {
+            return INSTANCES.get(sensLogUrl);
+        } else {
+            SensLogAuthService service = new SensLogAuthService(sensLogUrl);
+            INSTANCES.put(sensLogUrl, service);
+            return service;
+        }
+    }
+
+    private SensLogAuthService(String sensLogUrl) {
+        this.sensLogUrl = sensLogUrl;
+        this.authCookies = new HashMap<>();
+        this.httpClient = HttpClient.newHttpClient();
+        try {
+            this.baseUri = new URI(sensLogUrl);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("The config is in invalid format. " + e.getMessage());
+        }
+    }
+
+    public synchronized HttpCookie getAuthCookie(String username, String password) {
+        Tuple<String, String> credentials = Tuple.of(username, password);
+        HttpCookie authCookie = authCookies.get(credentials);
+        if (authCookie != null) {
+            return authCookie;
+        }
+
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(sensLogUrl, "/ControllerServlet")
+                        .addParam("username", username)
+                        .addParam("password", password)
+                        .build()
+                ).build();
+
+        HttpResponse response = httpClient.send(request);
+
+        if (response.isError()) {
+            return HttpCookie.empty();
+        }
+
+        final Type lastObsType = new TypeToken<Map<String, Object>>() {}.getType();
+        Map<String, Object> jsonResponse = jsonToObject(response.getBody(), lastObsType);
+
+        if (!jsonResponse.containsKey("sessionid")) {
+            return HttpCookie.empty();
+        }
+
+        String sessionId = (String) jsonResponse.get("sessionid");
+        String domain = baseUri.getHost();
+        String path = baseUri.getPath();
+        HttpCookie cookie = new HttpCookie("JSESSIONID", sessionId, domain, path);
+        authCookies.put(credentials, cookie);
+        return cookie;
+    }
+}

+ 10 - 0
connector-tools/src/main/java/cz/senslog/connector/tools/util/StringUtils.java

@@ -57,4 +57,14 @@ public final class StringUtils {
     public static boolean isNotBlank(String string) {
         return !isBlank(string);
     }
+
+    /**
+     * Creates string
+     * @param cut
+     * @return
+     */
+    public static String substringcut(String string, int cut) {
+        return string.substring(0, string.length()-cut);
+    }
+
 }

+ 10 - 0
docker-compose.yaml

@@ -42,3 +42,13 @@ services:
         APP_PARAMS: -cf config/fieldclimateSenslog10.yaml
         DEBUG: "false"
         LOG_MONITOR: "false"
+
+  alps1:
+    container_name: alapansSenslog1
+    build:
+      dockerfile: docker/Dockerfile
+      context: .
+      args:
+        MAVEN_PROFILE: AlapansSenslog1
+    environment:
+      APP_PARAMS: -cf config/alapansSenslog.yaml

+ 1 - 0
docker/Dockerfile

@@ -1,3 +1,4 @@
+#FROM maven:3.8.4-openjdk-17
 FROM zenika/alpine-maven:3-jdk8
 
 ARG MAVEN_PROFILE

+ 16 - 20
pom.xml

@@ -5,7 +5,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>cz.senslog</groupId>
-    <artifactId>connectors</artifactId>
+    <artifactId>connector-period</artifactId>
     <packaging>pom</packaging>
 
     <version>1.0-SNAPSHOT</version>
@@ -16,7 +16,7 @@
             <id>LoraWanSenslog1</id>
             <modules>
                 <module>connector-fetch-azure</module>
-                <module>connector-push-rest-senslog-v1</module>
+                <module>connector-push-senslog-v1</module>
             </modules>
         </profile>
 
@@ -24,7 +24,7 @@
             <id>FieldClimateSenslog1</id>
             <modules>
                 <module>connector-fetch-fieldclimate</module>
-                <module>connector-push-rest-senslog-v1</module>
+                <module>connector-push-senslog-v1</module>
             </modules>
         </profile>
 
@@ -40,7 +40,15 @@
             <id>DrutesSenslog1</id>
             <modules>
                 <module>connector-fetch-drutes</module>
-                <module>connector-push-rest-senslog-v1</module>
+                <module>connector-push-senslog-v1</module>
+            </modules>
+        </profile>
+
+        <profile>
+            <id>AlapansSenslog1</id>
+            <modules>
+                <module>connector-fetch-alapans</module>
+                <module>connector-push-senslog-v1</module>
             </modules>
         </profile>
 
@@ -51,11 +59,12 @@
             </activation>
             <modules>
                 <module>connector-fetch-azure</module>
-                <module>connector-push-rest-senslog-v1</module>
+                <module>connector-push-senslog-v1</module>
                 <module>connector-fetch-fieldclimate</module>
                 <module>connector-fetch-senslog-v1</module>
                 <module>connector-push-afarcloud</module>
                 <module>connector-fetch-drutes</module>
+                <module>connector-fetch-alapans</module>
             </modules>
         </profile>
     </profiles>
@@ -67,6 +76,8 @@
         <module>connector-push-api</module>
         <module>connector-fetch-drutes</module>
         <module>connector-tools</module>
+        <module>connector-fetch-demo</module>
+        <module>connector-fetch-alapans</module>
     </modules>
 
     <properties>
@@ -75,21 +86,6 @@
         <java.version>1.8</java.version>
     </properties>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter</artifactId>
-            <version>5.4.2</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <version>3.0.0</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
     <build>
         <plugins>
             <plugin>