Procházet zdrojové kódy

Session task, preparation for database, small fixes

Lukas Cerny před 6 roky
rodič
revize
1668beac50
55 změnil soubory, kde provedl 1217 přidání a 268 odebrání
  1. 20 11
      config/fieldclimateSenslog1.yaml
  2. 11 8
      config/lorawanSenslog1.yaml
  3. 50 0
      config/test_fieldclimateSenslog1.yaml
  4. 6 6
      connector-app/src/main/java/cz/senslog/connector/app/config/ConfigurationServiceImpl.java
  5. 26 8
      connector-app/src/main/java/cz/senslog/connector/app/config/Connector.java
  6. 33 24
      connector-app/src/main/java/cz/senslog/connector/app/config/ConnectorBuilder.java
  7. 10 1
      connector-app/src/main/java/cz/senslog/connector/app/config/DatabaseBuilderImpl.java
  8. 21 2
      connector-app/src/main/java/cz/senslog/connector/app/config/DatabaseConfigurationServiceImpl.java
  9. 18 27
      connector-app/src/main/java/cz/senslog/connector/app/config/FileConfigurationServiceImpl.java
  10. 4 4
      connector-app/src/main/java/cz/senslog/connector/app/config/api/ConfigurationService.java
  11. 7 0
      connector-app/src/main/java/cz/senslog/connector/app/config/api/DatabaseBuilder.java
  12. 39 32
      connector-app/src/test/java/cz/senslog/connector/app/config/ConnectorBuilderTest.java
  13. 11 4
      connector-app/src/test/java/cz/senslog/connector/app/config/FileConfigurationServiceImplTest.java
  14. 12 0
      connector-app/src/test/resources/test_valid_config.yaml
  15. 7 0
      connector-common/pom.xml
  16. 11 11
      connector-common/src/main/java/cz/senslog/connector/config/model/ConnectorDescriptor.java
  17. 14 5
      connector-common/src/main/java/cz/senslog/connector/config/model/PropertyConfig.java
  18. 8 0
      connector-common/src/main/java/cz/senslog/connector/exception/ModuleInterruptedException.java
  19. 8 0
      connector-common/src/main/java/cz/senslog/connector/exception/ParseException.java
  20. 57 0
      connector-common/src/main/java/cz/senslog/connector/interceptor/AbstractMethodInterceptor.java
  21. 23 0
      connector-common/src/main/java/cz/senslog/connector/interceptor/ProxyUtils.java
  22. 11 14
      connector-common/src/main/java/cz/senslog/connector/json/BasicJson.java
  23. 22 0
      connector-common/src/main/java/cz/senslog/connector/json/BasicJsonDeserializer.java
  24. 6 0
      connector-common/src/main/java/cz/senslog/connector/json/FormatFunction.java
  25. 23 0
      connector-common/src/main/java/cz/senslog/connector/util/Arrays.java
  26. 1 0
      connector-common/src/main/java/cz/senslog/connector/util/ClassUtils.java
  27. 14 0
      connector-common/src/main/java/cz/senslog/connector/util/MethodExplorer.java
  28. 2 2
      connector-common/src/main/java/cz/senslog/connector/util/Tuple.java
  29. 3 3
      connector-common/src/test/java/cz/senslog/connector/config/model/ConnectorDescriptorTest.java
  30. 1 1
      connector-common/src/test/java/cz/senslog/connector/config/model/PropertyConfigTest.java
  31. 4 4
      connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetchProvider.java
  32. 5 2
      connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetcher.java
  33. 48 0
      connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ExecutableFetcher.java
  34. 47 0
      connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/FetchProxySession.java
  35. 7 6
      connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/AzureConfig.java
  36. 11 7
      connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/AzureFetcher.java
  37. 4 2
      connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/ConnectorFetchAzureProvider.java
  38. 5 4
      connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/ConnectorFetchAzureProviderTest.java
  39. 20 5
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/ConnectorFetchFieldClimateProvider.java
  40. 8 0
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateConfig.java
  41. 22 25
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcher.java
  42. 100 0
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateProxySession.java
  43. 75 0
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSession.java
  44. 29 0
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSessionProxyConfig.java
  45. 65 23
      connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcherTest.java
  46. 127 0
      connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateProxySessionTest.java
  47. 67 0
      connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSessionTest.java
  48. 14 0
      connector-model/src/main/java/cz/senslog/connector/model/api/ProxySessionModel.java
  49. 1 14
      connector-model/src/main/java/cz/senslog/connector/model/converter/AzureModelSenslogV1ModelConverter.java
  50. 44 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/AzureUnitConverter.java
  51. 6 1
      connector-model/src/main/java/cz/senslog/connector/model/converter/FieldClimateModelSenslogV1ModelConverter.java
  52. 20 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/FieldClimateUnitConverter.java
  53. 8 6
      connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Pusher.java
  54. 1 1
      connector-push-rest-senslog-v1/src/test/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConfigTest.java
  55. 0 5
      docker-compose.yaml

+ 20 - 11
config/fieldclimateSenslog1.yaml

@@ -1,41 +1,50 @@
+api1: &fieldClimateApiDomain
+  domain: "https://api.fieldclimate.com/v1"
+
+api2: &senslogApiDomain
+  domain: "http://51.15.45.95:8080/senslog1"
+
 settings:
 settings:
   - SenslogV1:
   - SenslogV1:
       name: "Senslog V1"
       name: "Senslog V1"
       provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
       provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
       host:
       host:
-        domain: "http://51.15.45.95:8080/senslog1"
+        <<: *senslogApiDomain
         path: "FeederServlet"
         path: "FeederServlet"
 
 
   - Fieldclimate:
   - Fieldclimate:
       name: "FieldClimate: Pessl Instruments"
       name: "FieldClimate: Pessl Instruments"
       provider: "cz.senslog.connector.fetch.fieldclimate.ConnectorFetchFieldClimateProvider"
       provider: "cz.senslog.connector.fetch.fieldclimate.ConnectorFetchFieldClimateProvider"
       startDate: 2020-01-07T15:00:00.000
       startDate: 2020-01-07T15:00:00.000
-#      startDate: 2019-11-07T23:00:00.000
-      period: 12 # in hours
-
-      <<: &apiDomain
-        domain: "https://api.fieldclimate.com/v1"
+      period: 5 # in hours
 
 
       authentication:
       authentication:
         publicKey: "3737ed4fe98fae975e54991216ed473c8d7db48662deff19"
         publicKey: "3737ed4fe98fae975e54991216ed473c8d7db48662deff19"
         privateKey: "ed2e4abacdaad1d542eeabcec4ee4f6c8fbf3b8bb167b84b"
         privateKey: "ed2e4abacdaad1d542eeabcec4ee4f6c8fbf3b8bb167b84b"
 
 
       stationsHost:
       stationsHost:
-        <<: *apiDomain
+        <<: *fieldClimateApiDomain
         path: "/user/stations"
         path: "/user/stations"
 
 
       stationDataHost:
       stationDataHost:
-        <<: *apiDomain
+        <<: *fieldClimateApiDomain
         path: "/data/normal/{station_id}/raw/from/{from}/to/{to}"
         path: "/data/normal/{station_id}/raw/from/{from}/to/{to}"
 
 
       stationTimeRangeHost:
       stationTimeRangeHost:
-        <<: *apiDomain
+        <<: *fieldClimateApiDomain
         path: "/data/{station_id}"
         path: "/data/{station_id}"
 
 
+      sessionProxy:
+        user: "vilcini"
+        group: "vilcini"
+        lastObservationHost:
+          <<: *senslogApiDomain
+          path: "SensorService"
+
 
 
 connectors:
 connectors:
     - FieldclimateToV1:
     - FieldclimateToV1:
         fetcher: "Fieldclimate"
         fetcher: "Fieldclimate"
         pusher: "SenslogV1"
         pusher: "SenslogV1"
-        period: 10
-        initDelay: 1
+        period: 600 # 10 minutes
+        initDelay: 5

+ 11 - 8
config/lorawanSenslog1.yaml

@@ -1,3 +1,9 @@
+api1: &azureApiDomain
+    domain: "https://iotlorawan.azurewebsites.net"
+
+api2: &senslogApiDomain
+    domain: "http://foodie.lesprojekt.cz:8080/MapLogOT"
+
 settings:
 settings:
     - AzureLoraWan:
     - AzureLoraWan:
         name: "IoT LoraWan"
         name: "IoT LoraWan"
@@ -6,20 +12,17 @@ settings:
 #        endDate: 2019-09-30T11:00:00.000
 #        endDate: 2019-09-30T11:00:00.000
         limitPerSensor: 100
         limitPerSensor: 100
 
 
-        <<: &apiDomain
-            domain: "https://iotlorawan.azurewebsites.net"
-        
         sensorInfoHost:
         sensorInfoHost:
-            <<: *apiDomain
+            <<: *azureApiDomain
             path: "api/sensors"
             path: "api/sensors"
         
         
         sensorDataHost:
         sensorDataHost:
-            <<: *apiDomain
+            <<: *azureApiDomain
             path: "api/sensordata"
             path: "api/sensordata"
         
         
         authentication:
         authentication:
             host:
             host:
-                <<: *apiDomain
+                <<: *azureApiDomain
                 path: "api/accounts/login"
                 path: "api/accounts/login"
             username: "netluky@ima.cz"
             username: "netluky@ima.cz"
             password: "SensLogIMA1"
             password: "SensLogIMA1"
@@ -29,8 +32,8 @@ settings:
         name: "Senslog V1"
         name: "Senslog V1"
         provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
         provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
         host:
         host:
-            domain: "http://foodie.lesprojekt.cz:8080"
-            path: "MapLogOT/FeederServlet"
+            <<: *senslogApiDomain
+            path: "FeederServlet"
 
 
 connectors:
 connectors:
     - AzureSenslogV1:
     - AzureSenslogV1:

+ 50 - 0
config/test_fieldclimateSenslog1.yaml

@@ -0,0 +1,50 @@
+api1: &fieldClimateApiDomain
+  domain: "https://api.fieldclimate.com/v1"
+
+api2: &senslogApiDomain
+  domain: "http://127.0.0.1:9080"
+
+settings:
+  - SenslogV1:
+      name: "Senslog V1"
+      provider: "cz.senslog.connector.push.rest.senslog.v1.SenslogV1ConnectorPushProvider"
+      host:
+        <<: *senslogApiDomain
+        path: "FeederServlet"
+
+  - Fieldclimate:
+      name: "FieldClimate: Pessl Instruments"
+      provider: "cz.senslog.connector.fetch.fieldclimate.ConnectorFetchFieldClimateProvider"
+      startDate: 2019-11-01T00:00:00.000
+      period: 12 # hours
+
+      authentication:
+        publicKey: "3737ed4fe98fae975e54991216ed473c8d7db48662deff19"
+        privateKey: "ed2e4abacdaad1d542eeabcec4ee4f6c8fbf3b8bb167b84b"
+
+      stationsHost:
+        <<: *fieldClimateApiDomain
+        path: "/user/stations"
+
+      stationDataHost:
+        <<: *fieldClimateApiDomain
+        path: "/data/normal/{station_id}/raw/from/{from}/to/{to}"
+
+      stationTimeRangeHost:
+        <<: *fieldClimateApiDomain
+        path: "/data/{station_id}"
+
+      sessionProxy:
+        user: "vilcini"
+        group: "vilcini"
+        lastObservationHost:
+          <<: *senslogApiDomain
+          path: "SensorService"
+
+
+connectors:
+    - FieldclimateToV1:
+        fetcher: "Fieldclimate"
+        pusher: "SenslogV1"
+        period: 10
+        initDelay: 2

+ 6 - 6
connector-app/src/main/java/cz/senslog/connector/app/config/ConfigurationServiceImpl.java

@@ -20,7 +20,7 @@ import java.util.Set;
 public abstract class ConfigurationServiceImpl implements ConfigurationService {
 public abstract class ConfigurationServiceImpl implements ConfigurationService {
 
 
     private Set<ConnectorDescriptor> connectorDescriptors;
     private Set<ConnectorDescriptor> connectorDescriptors;
-    private Map<Class<?>, DefaultConfig> configurations;
+    private Map<String, DefaultConfig> configurations;
 
 
     ConfigurationServiceImpl() {
     ConfigurationServiceImpl() {
         this.connectorDescriptors = new HashSet<>();
         this.connectorDescriptors = new HashSet<>();
@@ -31,9 +31,9 @@ public abstract class ConfigurationServiceImpl implements ConfigurationService {
         connectorDescriptors.add(descriptor);
         connectorDescriptors.add(descriptor);
     }
     }
 
 
-    protected void addProviderConfiguration(Class<?> aClass, DefaultConfig config) {
-        if (!configurations.containsKey(aClass)) {
-            configurations.put(aClass, config);
+    protected void addProviderConfiguration(String providerId, DefaultConfig config) {
+        if (!configurations.containsKey(providerId)) {
+            configurations.put(providerId, config);
         }
         }
     }
     }
 
 
@@ -43,7 +43,7 @@ public abstract class ConfigurationServiceImpl implements ConfigurationService {
     }
     }
 
 
     @Override
     @Override
-    public DefaultConfig getConfigForClass(Class<?> aClass) {
-        return configurations.get(aClass);
+    public DefaultConfig getConfigForProviderId(String providerId) {
+        return configurations.get(providerId);
     }
     }
 }
 }

+ 26 - 8
connector-app/src/main/java/cz/senslog/connector/app/config/Connector.java

@@ -1,12 +1,14 @@
 package cz.senslog.connector.app.config;
 package cz.senslog.connector.app.config;
 
 
-import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.exception.ModuleInterruptedException;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
 
 
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
@@ -41,10 +43,10 @@ public final class Connector {
     private final String name;
     private final String name;
 
 
     /** Instance of a fetcher that provides data. */
     /** Instance of a fetcher that provides data. */
-    private final ConnectorFetcher<? super AbstractModel> fetcher;
+    private final ExecutableFetcher<? super AbstractModel> fetcherExecutor;
 
 
     /** Instance of a pusher that receives data. */
     /** Instance of a pusher that receives data. */
-    private final ConnectorPusher<? super AbstractModel> pusher;
+    private final ConnectorPusher<? super AbstractModel> pusher; // TODO refactor to a ExecutableFetcher
 
 
     /** Converter between fetch and push. */
     /** Converter between fetch and push. */
     private final Converter<? super AbstractModel, ? super AbstractModel> converter;
     private final Converter<? super AbstractModel, ? super AbstractModel> converter;
@@ -58,21 +60,21 @@ public final class Connector {
     /**
     /**
      * Constructor allows to set all attributes.
      * Constructor allows to set all attributes.
      * @param name - name of the connector.
      * @param name - name of the connector.
-     * @param fetcher - instance of fetcher.
+     * @param fetcherExecutor - instance of fetcher.
      * @param pusher - instance of pusher.
      * @param pusher - instance of pusher.
      * @param converter - instance of converter.
      * @param converter - instance of converter.
      * @param period - period for scheduling.
      * @param period - period for scheduling.
      */
      */
     public Connector(
     public Connector(
             String name,
             String name,
-            ConnectorFetcher<? super AbstractModel> fetcher,
+            ExecutableFetcher<? super AbstractModel> fetcherExecutor,
             ConnectorPusher<? super AbstractModel> pusher,
             ConnectorPusher<? super AbstractModel> pusher,
             Converter<? super AbstractModel, ? super AbstractModel> converter,
             Converter<? super AbstractModel, ? super AbstractModel> converter,
             Integer period,
             Integer period,
             Integer initDelay
             Integer initDelay
     ) {
     ) {
         this.name = name;
         this.name = name;
-        this.fetcher = fetcher;
+        this.fetcherExecutor = fetcherExecutor;
         this.pusher = pusher;
         this.pusher = pusher;
         this.converter = converter;
         this.converter = converter;
         this.period = ofNullable(period);
         this.period = ofNullable(period);
@@ -108,7 +110,7 @@ public final class Connector {
      * @return runnable task
      * @return runnable task
      */
      */
     public Runnable getTask() {
     public Runnable getTask() {
-        return () -> of(fetcher::fetch).pipe(converter::convert).end(pusher::push);
+        return () -> of(fetcherExecutor::execute).pipe(converter::convert).end(pusher::push);
     }
     }
 
 
     /**
     /**
@@ -129,11 +131,27 @@ public final class Connector {
         new Thread(() -> {
         new Thread(() -> {
             try {
             try {
                 future.get();
                 future.get();
-            } catch (Exception e) {
+            } catch (ModuleInterruptedException e) {
                 logger.warn(e.getMessage());
                 logger.warn(e.getMessage());
+            } catch (Exception e) {
+                logger.catching(e);
+            } finally {
                 future.cancel(true);
                 future.cancel(true);
                 latch.countDown();
                 latch.countDown();
             }
             }
         }, "thread-"+getName()).start();
         }, "thread-"+getName()).start();
     }
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Connector connector = (Connector) o;
+        return Objects.equals(name, connector.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name);
+    }
 }
 }

+ 33 - 24
connector-app/src/main/java/cz/senslog/connector/app/config/ConnectorBuilder.java

@@ -5,6 +5,7 @@ import cz.senslog.connector.config.model.ConnectorDescriptor;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.ConverterProvider;
 import cz.senslog.connector.model.api.ConverterProvider;
@@ -66,50 +67,50 @@ public final class ConnectorBuilder {
 
 
     /**
     /**
      * Creates and returns new instance of fetcher.
      * Creates and returns new instance of fetcher.
-     * @param fetchProviderClass - class of fetcher provider.
-     * @return new instance of fetcher {@code ConnectorFetcher}.
+     * @param fetchProviderId - id of fetcher provider.
+     * @return new instance of fetcher {@code ExecutableFetcher}.
      * @throws Exception throws if the fetch provider does not exist or any configuration does not exists.
      * @throws Exception throws if the fetch provider does not exist or any configuration does not exists.
      */
      */
-    private ConnectorFetcher getFetcherInstance(Class<?> fetchProviderClass) throws Exception {
-        logger.debug("Creating a new instance of fetcher for {}.", fetchProviderClass);
+    private ExecutableFetcher createFetcherExecutor(String fetchProviderId) throws Exception {
+        logger.debug("Creating a new instance of fetcher for {}.", fetchProviderId);
 
 
-        ConnectorFetchProvider provider = serviceProvider.getFetchProvider(fetchProviderClass);
-        if (provider == null) {
+        DefaultConfig config = configService.getConfigForProviderId(fetchProviderId);
+        if (config == null) {
             throw logger.throwing(new Exception(format(
             throw logger.throwing(new Exception(format(
-                    "Can not find a fetch provider instance for the %s.", fetchProviderClass
+                    "Can not find a default settings for the provider %s.", fetchProviderId
             )));
             )));
         }
         }
 
 
-        DefaultConfig config = configService.getConfigForClass(provider.getClass());
-        if (config == null) {
+        ConnectorFetchProvider provider = serviceProvider.getFetchProvider(config.getProvider());
+        if (provider == null) {
             throw logger.throwing(new Exception(format(
             throw logger.throwing(new Exception(format(
-                    "Can not find a default settings for the provider %s.", provider.getClass()
+                    "Can not find a fetch provider instance for the %s.", config.getProvider()
             )));
             )));
         }
         }
 
 
-        return provider.createFetcher(config);
+        return provider.createExecutableFetcher(config);
     }
     }
 
 
     /**
     /**
      * Creates and returns new instance of pusher.
      * Creates and returns new instance of pusher.
-     * @param pushProviderClass - class of push provider.
+     * @param pushProviderId - class of push provider.
      * @return new instance of pusher {@code ConnectorPusher}.
      * @return new instance of pusher {@code ConnectorPusher}.
      * @throws Exception throws if the push provider does not exist or any configuration does not exists.
      * @throws Exception throws if the push provider does not exist or any configuration does not exists.
      */
      */
-    private ConnectorPusher getPusherInstance(Class<?> pushProviderClass) throws Exception {
-        logger.debug("Creating a new instance of pusher for {}.", pushProviderClass);
+    private ConnectorPusher getPusherInstance(String pushProviderId) throws Exception {
+        logger.debug("Creating a new instance of pusher for {}.", pushProviderId);
 
 
-        ConnectorPushProvider provider = serviceProvider.getPushProvider(pushProviderClass);
-        if (provider == null) {
+        DefaultConfig config = configService.getConfigForProviderId(pushProviderId);
+        if (config == null) {
             throw logger.throwing(new Exception(format(
             throw logger.throwing(new Exception(format(
-                    "Can not find a push provider instance for the %s.", pushProviderClass
+                    "Can not find a default settings for the provider %s.", pushProviderId
             )));
             )));
         }
         }
 
 
-        DefaultConfig config = configService.getConfigForClass(provider.getClass());
-        if (config == null) {
+        ConnectorPushProvider provider = serviceProvider.getPushProvider(config.getProvider());
+        if (provider == null) {
             throw logger.throwing(new Exception(format(
             throw logger.throwing(new Exception(format(
-                    "Can not find a default settings for the provider %s.", provider.getClass()
+                    "Can not find a push provider instance for the %s.", config.getProvider()
             )));
             )));
         }
         }
 
 
@@ -137,8 +138,10 @@ public final class ConnectorBuilder {
                 logger.debug("Getting descriptors for a new '{}' connector connection.", connDesc.getName());
                 logger.debug("Getting descriptors for a new '{}' connector connection.", connDesc.getName());
                 logger.debug("Connector: {}", connDesc);
                 logger.debug("Connector: {}", connDesc);
 
 
-                ConnectorFetcher fetcher = getFetcherInstance(connDesc.getFetcher());
-                ConnectorPusher pusher = getPusherInstance(connDesc.getPusher());
+                ExecutableFetcher fetcherExecutor = createFetcherExecutor(connDesc.getFetcherId());
+                ConnectorFetcher fetcher = fetcherExecutor.getRawFetcher();
+
+                ConnectorPusher pusher = getPusherInstance(connDesc.getPusherId());
 
 
                 Class<? extends AbstractModel> inputModel = getAbstractModelFromGeneric(fetcher.getClass());
                 Class<? extends AbstractModel> inputModel = getAbstractModelFromGeneric(fetcher.getClass());
                 Class<? extends AbstractModel> outputModel = getAbstractModelFromGeneric(pusher.getClass());
                 Class<? extends AbstractModel> outputModel = getAbstractModelFromGeneric(pusher.getClass());
@@ -157,7 +160,7 @@ public final class ConnectorBuilder {
                 pusher.init();
                 pusher.init();
 
 
                 logger.debug("Creating a new {} connector.", connDesc.getName());
                 logger.debug("Creating a new {} connector.", connDesc.getName());
-                Connector connector = new Connector(connDesc.getName(), fetcher, pusher, converter, connDesc.getPeriod(), connDesc.getDelay());
+                Connector connector = new Connector(connDesc.getName(), fetcherExecutor, pusher, converter, connDesc.getPeriod(), connDesc.getDelay());
 
 
                 logger.debug("Saving the {} connector.", connDesc.getName());
                 logger.debug("Saving the {} connector.", connDesc.getName());
                 connectors.add(connector);
                 connectors.add(connector);
@@ -202,7 +205,13 @@ public final class ConnectorBuilder {
             )));
             )));
         }
         }
 
 
-        Type classModelType = classArgumentTypes[0];
+        Type classModelType; // TODO refactor
+        if (classArgumentTypes.length == 2) {
+            classModelType = classArgumentTypes[1];
+        } else {
+            classModelType = classArgumentTypes[0];
+        }
+
         if (!(classModelType instanceof Class)) {
         if (!(classModelType instanceof Class)) {
             throw logger.throwing(new Exception(format(
             throw logger.throwing(new Exception(format(
                     "%s contains generic parameters which are not instance of %s.", aClass, Class.class
                     "%s contains generic parameters which are not instance of %s.", aClass, Class.class

+ 10 - 1
connector-app/src/main/java/cz/senslog/connector/app/config/DatabaseBuilderImpl.java

@@ -22,6 +22,9 @@ public class DatabaseBuilderImpl implements DatabaseBuilder {
     /** Password to a database. */
     /** Password to a database. */
     private String password;
     private String password;
 
 
+    /** Name of connector in database. */
+    private String connectorName;
+
     @Override
     @Override
     public DatabaseBuilder connectionUrl(String connectionUrl) {
     public DatabaseBuilder connectionUrl(String connectionUrl) {
         this.connectionUrl = connectionUrl;
         this.connectionUrl = connectionUrl;
@@ -41,7 +44,13 @@ public class DatabaseBuilderImpl implements DatabaseBuilder {
     }
     }
 
 
     @Override
     @Override
+    public DatabaseBuilder connectorName(String connectorName) {
+        this.connectorName = connectorName;
+        return this;
+    }
+
+    @Override
     public DatabaseConfigurationService build() {
     public DatabaseConfigurationService build() {
-        return new DatabaseConfigurationServiceImpl(connectionUrl, username, password);
+        return new DatabaseConfigurationServiceImpl(connectionUrl, username, password, connectorName);
     }
     }
 }
 }

+ 21 - 2
connector-app/src/main/java/cz/senslog/connector/app/config/DatabaseConfigurationServiceImpl.java

@@ -1,9 +1,14 @@
 package cz.senslog.connector.app.config;
 package cz.senslog.connector.app.config;
 
 
+import cz.senslog.connector.app.config.api.ConfigurationService;
 import cz.senslog.connector.app.config.api.DatabaseConfigurationService;
 import cz.senslog.connector.app.config.api.DatabaseConfigurationService;
+import cz.senslog.connector.config.model.ConnectorDescriptor;
+import cz.senslog.connector.config.model.DefaultConfig;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
 
 
+import java.util.Set;
+
 import static java.lang.String.format;
 import static java.lang.String.format;
 
 
 /**
 /**
@@ -13,7 +18,7 @@ import static java.lang.String.format;
  * @version 1.0
  * @version 1.0
  * @since 1.0
  * @since 1.0
  */
  */
-class DatabaseConfigurationServiceImpl extends ConfigurationServiceImpl implements DatabaseConfigurationService {
+class DatabaseConfigurationServiceImpl implements ConfigurationService, DatabaseConfigurationService {
 
 
     private static Logger logger = LogManager.getLogger(DatabaseConfigurationServiceImpl.class);
     private static Logger logger = LogManager.getLogger(DatabaseConfigurationServiceImpl.class);
 
 
@@ -26,16 +31,20 @@ class DatabaseConfigurationServiceImpl extends ConfigurationServiceImpl implemen
     /** Password to a database. */
     /** Password to a database. */
     private final String password;
     private final String password;
 
 
+    /** Name of connector in a database. */
+    private final String connectorName;
+
     /**
     /**
      * Constructor sets all attributes.
      * Constructor sets all attributes.
      * @param connectionUrl - connection url.
      * @param connectionUrl - connection url.
      * @param username - username.
      * @param username - username.
      * @param password - password.
      * @param password - password.
      */
      */
-    DatabaseConfigurationServiceImpl(String connectionUrl, String username, String password) {
+    DatabaseConfigurationServiceImpl(String connectionUrl, String username, String password, String connectorName) {
         this.connectionUrl = connectionUrl;
         this.connectionUrl = connectionUrl;
         this.username = username;
         this.username = username;
         this.password = password;
         this.password = password;
+        this.connectorName = connectorName;
     }
     }
 
 
     @Override
     @Override
@@ -44,4 +53,14 @@ class DatabaseConfigurationServiceImpl extends ConfigurationServiceImpl implemen
                 "%s#connect() is not implemented.", DatabaseConfigurationServiceImpl.class
                 "%s#connect() is not implemented.", DatabaseConfigurationServiceImpl.class
         )));
         )));
     }
     }
+
+    @Override
+    public Set<ConnectorDescriptor> getConnectorDescriptors() {
+        return null;
+    }
+
+    @Override
+    public DefaultConfig getConfigForProviderId(String providerId) {
+        return null;
+    }
 }
 }

+ 18 - 27
connector-app/src/main/java/cz/senslog/connector/app/config/FileConfigurationServiceImpl.java

@@ -87,12 +87,20 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
             throw new FileNotFoundException(fileName + " does not exist");
             throw new FileNotFoundException(fileName + " does not exist");
         }
         }
 
 
+        Map<Object, Object> properties;
+
         logger.debug("Opening the file '{}'.", fileName);
         logger.debug("Opening the file '{}'.", fileName);
-        InputStream fileStream = Files.newInputStream(filePath);
+        try (InputStream fileStream = Files.newInputStream(filePath)) {
+            logger.debug("Parsing the yaml file '{}'.", fileName);
+            properties = new Yaml().load(fileStream);
+            logger.debug("The configuration yaml file '{}' was parsed successfully.", fileName);
+        }
 
 
-        logger.debug("Parsing the yaml file '{}'.", fileName);
-        Map<Object, Object> properties = new Yaml().load(fileStream);
-        logger.debug("The configuration yaml file '{}' was parsed successfully.", fileName);
+        if (properties == null || properties.isEmpty()) {
+            throw new IOException(String.format(
+                    "The configuration yaml file %s is empty or was not loaded successfully. ", fileName
+            ));
+        }
 
 
         logger.debug("Getting 'settings' property from the configuration file.");
         logger.debug("Getting 'settings' property from the configuration file.");
         List settingsList = (List)properties.get("settings");
         List settingsList = (List)properties.get("settings");
@@ -101,16 +109,15 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
         List connectorsList = (List) properties.get("connectors");
         List connectorsList = (List) properties.get("connectors");
 
 
         logger.debug("Starting to parse all connector descriptors from the config file.");
         logger.debug("Starting to parse all connector descriptors from the config file.");
-        Map<String, Class<?>> settings = settings(settingsList);
+        settings(settingsList);
 
 
         logger.debug("Starting to create all connector connection from the configuration file.");
         logger.debug("Starting to create all connector connection from the configuration file.");
-        createConnectorDescriptors(connectorsList, settings);
+        createConnectorDescriptors(connectorsList);
 
 
         logger.info("The configuration file '{}' was parsed successfully.", fileName);
         logger.info("The configuration file '{}' was parsed successfully.", fileName);
     }
     }
 
 
-    private Map<String, Class<?>> settings(List settingsList) throws InvalidPropertiesFormatException {
-        Map<String, Class<?>> idDescClass = new HashMap<>();
+    private void settings(List settingsList) throws InvalidPropertiesFormatException {
 
 
         logger.debug("Parsing 'settings' from the configuration file.");
         logger.debug("Parsing 'settings' from the configuration file.");
         for (Object settings : settingsList) {
         for (Object settings : settingsList) {
@@ -178,15 +185,13 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
                 }
                 }
 
 
                 logger.debug("Saving the settings descriptor '{}'.", descriptorId);
                 logger.debug("Saving the settings descriptor '{}'.", descriptorId);
-                addProviderConfiguration(providerClass, defaultConfig);
+                addProviderConfiguration(descriptorId, defaultConfig);
 
 
-                idDescClass.put(descriptorId, providerClass);
             }
             }
         }
         }
-        return idDescClass;
     }
     }
 
 
-    private void createConnectorDescriptors(List connectorsList, Map<String, Class<?>> settings) throws InvalidPropertiesFormatException {
+    private void createConnectorDescriptors(List connectorsList) throws InvalidPropertiesFormatException {
 
 
         logger.debug("Parsing 'connectors' from the configuration file.");
         logger.debug("Parsing 'connectors' from the configuration file.");
         for (Object connector : connectorsList) {
         for (Object connector : connectorsList) {
@@ -227,13 +232,6 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
                     ));
                     ));
                 }
                 }
 
 
-                Class<?> fetchProviderClass = settings.get(fetchProviderId);
-                if (fetchProviderClass == null) {
-                    throw logger.throwing(new NoSuchElementException(
-                            "Identifier for property 'fetcher' was not found in settings descriptors."
-                    ));
-                }
-
                 logger.debug("Getting the push class provider for the connector ID '{}'.", descriptorId);
                 logger.debug("Getting the push class provider for the connector ID '{}'.", descriptorId);
                 String pushProviderId = (String) connectorValuesMap.get("pusher");
                 String pushProviderId = (String) connectorValuesMap.get("pusher");
                 if (pushProviderId == null) {
                 if (pushProviderId == null) {
@@ -242,13 +240,6 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
                     ));
                     ));
                 }
                 }
 
 
-                Class<?> pushProviderClass = settings.get(pushProviderId);
-                if (pushProviderClass == null) {
-                    throw logger.throwing(new NoSuchElementException(
-                            "Identifier for property 'pusher' was not found in settings descriptors."
-                    ));
-                }
-
                 logger.debug("Getting property 'period' from the connector descriptor '{}'.", descriptorId);
                 logger.debug("Getting property 'period' from the connector descriptor '{}'.", descriptorId);
                 Integer period = (Integer) connectorValuesMap.get("period");
                 Integer period = (Integer) connectorValuesMap.get("period");
 
 
@@ -256,7 +247,7 @@ class FileConfigurationServiceImpl extends ConfigurationServiceImpl implements F
                 Integer delay = (Integer) connectorValuesMap.get("initDelay");
                 Integer delay = (Integer) connectorValuesMap.get("initDelay");
 
 
                 logger.debug("Creating a new ConnectorDescriptor class for the connector descriptor '{}'.", descriptorId);
                 logger.debug("Creating a new ConnectorDescriptor class for the connector descriptor '{}'.", descriptorId);
-                ConnectorDescriptor connDesc = new ConnectorDescriptor(descriptorId, fetchProviderClass, pushProviderClass, period, delay);
+                ConnectorDescriptor connDesc = new ConnectorDescriptor(descriptorId, fetchProviderId, pushProviderId, period, delay);
 
 
                 logger.debug("Saving the connector descriptor '{}'.", descriptorId);
                 logger.debug("Saving the connector descriptor '{}'.", descriptorId);
                 addConnectorDescriptor(connDesc);
                 addConnectorDescriptor(connDesc);

+ 4 - 4
connector-app/src/main/java/cz/senslog/connector/app/config/api/ConfigurationService.java

@@ -43,9 +43,9 @@ public interface ConfigurationService {
     Set<ConnectorDescriptor> getConnectorDescriptors();
     Set<ConnectorDescriptor> getConnectorDescriptors();
 
 
     /**
     /**
-     * Returns a configuration depends on input class.
-     * @param aClass - class for which configuration will be gotten.
-     * @return configuration for an input class.
+     * Returns a configuration depends on a provider id.
+     * @param providerId - identifier of provider.
+     * @return default configuration for an input provider id.
      */
      */
-    DefaultConfig getConfigForClass(Class<?> aClass);
+    DefaultConfig getConfigForProviderId(String providerId);
 }
 }

+ 7 - 0
connector-app/src/main/java/cz/senslog/connector/app/config/api/DatabaseBuilder.java

@@ -33,6 +33,13 @@ public interface DatabaseBuilder {
     DatabaseBuilder password(String password);
     DatabaseBuilder password(String password);
 
 
     /**
     /**
+     * Identifier of connector name in a configuration database.
+     * @param connectorName connector name.
+     * @return instance of builder {@code DatabaseBuilder}.
+     */
+    DatabaseBuilder connectorName(String connectorName);
+
+    /**
      * Creates a new instance with the configuration.
      * Creates a new instance with the configuration.
      * @return new instance of {@link DatabaseConfigurationService}.
      * @return new instance of {@link DatabaseConfigurationService}.
      */
      */

+ 39 - 32
connector-app/src/test/java/cz/senslog/connector/app/config/ConnectorBuilderTest.java

@@ -5,14 +5,17 @@ import cz.senslog.connector.config.model.ConnectorDescriptor;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.ConverterProvider;
 import cz.senslog.connector.model.api.ConverterProvider;
+import cz.senslog.connector.model.api.ProxySessionModel;
 import cz.senslog.connector.push.api.ConnectorPushProvider;
 import cz.senslog.connector.push.api.ConnectorPushProvider;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 
 
 import java.util.HashSet;
 import java.util.HashSet;
+import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 
 
 import static java.time.LocalDateTime.MAX;
 import static java.time.LocalDateTime.MAX;
@@ -26,10 +29,14 @@ class ConnectorBuilderTest {
     private static class InputModel extends AbstractModel{ InputModel() { super(MIN, MAX); }}
     private static class InputModel extends AbstractModel{ InputModel() { super(MIN, MAX); }}
     private static class OutputModel extends AbstractModel{ OutputModel() { super(MIN, MAX);}}
     private static class OutputModel extends AbstractModel{ OutputModel() { super(MIN, MAX);}}
 
 
-    private final ConnectorFetchProvider defaultFetchProvider = config -> new ConnectorFetcher<OutputModel>() {
+    private static class BasicSessionModel extends ProxySessionModel {
+        public BasicSessionModel() { super(false); }
+    }
+
+    private final ConnectorFetchProvider defaultFetchProvider = config -> ExecutableFetcher.create(new ConnectorFetcher<BasicSessionModel, OutputModel>() {
         @Override public void init() {}
         @Override public void init() {}
-        @Override public OutputModel fetch() { return new OutputModel(); }
-    };
+        @Override public OutputModel fetch(Optional<BasicSessionModel> session) { return new OutputModel(); }
+    });
 
 
     private final ConnectorPushProvider defaultPushProvider = config -> new ConnectorPusher<InputModel>() {
     private final ConnectorPushProvider defaultPushProvider = config -> new ConnectorPusher<InputModel>() {
         @Override public void init() {}
         @Override public void init() {}
@@ -46,12 +53,12 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -73,13 +80,13 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test1", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 3));
-        connectorDescriptors.add(new ConnectorDescriptor("Test2", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 2, 3));
+        connectorDescriptors.add(new ConnectorDescriptor("Test1", "FetcherId", "PusherId", 1, 3));
+        connectorDescriptors.add(new ConnectorDescriptor("Test2", "FetcherId", "PusherId", 2, 3));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -97,11 +104,11 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> null, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> null, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -118,13 +125,13 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         // fetch provider configuration does not exist -> null
         // fetch provider configuration does not exist -> null
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(null);
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(null);
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -142,11 +149,11 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> null);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> null);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -163,14 +170,14 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
 
 
         // fetch provider configuration does not exist -> null
         // fetch provider configuration does not exist -> null
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(null);
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(null);
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -187,12 +194,12 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -203,10 +210,10 @@ class ConnectorBuilderTest {
     void createConnectors_IncompatibleFetchModelClass_CreatedZeroConnector() {
     void createConnectors_IncompatibleFetchModelClass_CreatedZeroConnector() {
 
 
         // ConnectorFetcher does not contain model class as a generic parameter
         // ConnectorFetcher does not contain model class as a generic parameter
-         ConnectorFetchProvider fetchProvider = config -> new ConnectorFetcher() {
+         ConnectorFetchProvider fetchProvider = config -> ExecutableFetcher.create(new ConnectorFetcher() {
             @Override public void init() {}
             @Override public void init() {}
-            @Override public OutputModel fetch() { return new OutputModel(); }
-        };
+            @Override public AbstractModel fetch(Optional session) {return new OutputModel();}
+        });
 
 
         ConverterProvider converterProvider = mock(ConverterProvider.class);
         ConverterProvider converterProvider = mock(ConverterProvider.class);
         when(converterProvider.getConverter(OutputModel.class, InputModel.class)).thenReturn(
         when(converterProvider.getConverter(OutputModel.class, InputModel.class)).thenReturn(
@@ -215,12 +222,12 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> fetchProvider, aClass -> defaultPushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> fetchProvider, aClass -> defaultPushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", fetchProvider.getClass(), defaultPushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(fetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
-        when(configService.getConfigForClass(defaultPushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 
@@ -243,12 +250,12 @@ class ConnectorBuilderTest {
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> pushProvider);
         ServiceProvider serviceProvider = new ServiceProvider(aClass -> defaultFetchProvider, aClass -> pushProvider);
 
 
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
         Set<ConnectorDescriptor> connectorDescriptors = new HashSet<>();
-        connectorDescriptors.add(new ConnectorDescriptor("Test", defaultFetchProvider.getClass(), pushProvider.getClass(), 1, 2));
+        connectorDescriptors.add(new ConnectorDescriptor("Test", "FetcherId", "PusherId", 1, 2));
 
 
         ConfigurationService configService = mock(ConfigurationService.class);
         ConfigurationService configService = mock(ConfigurationService.class);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
         when(configService.getConnectorDescriptors()).thenReturn(connectorDescriptors);
-        when(configService.getConfigForClass(defaultFetchProvider.getClass())).thenReturn(mock(DefaultConfig.class));
-        when(configService.getConfigForClass(pushProvider.getClass())).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("FetcherId")).thenReturn(mock(DefaultConfig.class));
+        when(configService.getConfigForProviderId("PusherId")).thenReturn(mock(DefaultConfig.class));
 
 
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
         Set<Connector> connectors = ConnectorBuilder.init(serviceProvider, converterProvider, configService).createConnectors();
 
 

+ 11 - 4
connector-app/src/test/java/cz/senslog/connector/app/config/FileConfigurationServiceImplTest.java

@@ -3,6 +3,7 @@ package cz.senslog.connector.app.config;
 import cz.senslog.connector.app.config.api.FileConfigurationService;
 import cz.senslog.connector.app.config.api.FileConfigurationService;
 import cz.senslog.connector.config.model.ConnectorDescriptor;
 import cz.senslog.connector.config.model.ConnectorDescriptor;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
+import cz.senslog.connector.config.model.HostConfig;
 import cz.senslog.connector.exception.UnsupportedFileException;
 import cz.senslog.connector.exception.UnsupportedFileException;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 
 
@@ -34,19 +35,25 @@ class FileConfigurationServiceImplTest {
 
 
         ConnectorDescriptor descriptor = descriptors.iterator().next();
         ConnectorDescriptor descriptor = descriptors.iterator().next();
         assertEquals("ConnectorName", descriptor.getName());
         assertEquals("ConnectorName", descriptor.getName());
-        assertEquals(TestFetchProviderClass.class, descriptor.getFetcher());
-        assertEquals(TestPushProviderClass.class, descriptor.getPusher());
+        assertEquals("FetchProviderId", descriptor.getFetcherId());
+        assertEquals("PushProviderId", descriptor.getPusherId());
         assertEquals(100, descriptor.getPeriod());
         assertEquals(100, descriptor.getPeriod());
 
 
-        DefaultConfig fetchConfig = configService.getConfigForClass(TestFetchProviderClass.class);
+        DefaultConfig fetchConfig = configService.getConfigForProviderId("FetchProviderId");
         assertEquals("FetchProviderId", fetchConfig.getId());
         assertEquals("FetchProviderId", fetchConfig.getId());
         assertEquals(TestFetchProviderClass.class, fetchConfig.getProvider());
         assertEquals(TestFetchProviderClass.class, fetchConfig.getProvider());
         assertEquals("<name>", fetchConfig.getStringProperty("name"));
         assertEquals("<name>", fetchConfig.getStringProperty("name"));
+        HostConfig fetchHost = new HostConfig(fetchConfig.getPropertyConfig("host"));
+        assertEquals("<fetcher_api_domain>", fetchHost.getDomain());
+        assertEquals("<path>", fetchHost.getPath());
 
 
-        DefaultConfig pushConfig = configService.getConfigForClass(TestPushProviderClass.class);
+        DefaultConfig pushConfig = configService.getConfigForProviderId("PushProviderId");
         assertEquals("PushProviderId", pushConfig.getId());
         assertEquals("PushProviderId", pushConfig.getId());
         assertEquals(TestPushProviderClass.class, pushConfig.getProvider());
         assertEquals(TestPushProviderClass.class, pushConfig.getProvider());
         assertEquals("<name>", pushConfig.getStringProperty("name"));
         assertEquals("<name>", pushConfig.getStringProperty("name"));
+        HostConfig pushHost = new HostConfig(pushConfig.getPropertyConfig("host"));
+        assertEquals("<pusher_api_domain>", pushHost.getDomain());
+        assertEquals("<path>", pushHost.getPath());
     }
     }
 
 
     @Test
     @Test

+ 12 - 0
connector-app/src/test/resources/test_valid_config.yaml

@@ -1,11 +1,23 @@
+feMainDomain: &fetcherApiDomain
+  domain: "<fetcher_api_domain>"
+
+puMainDomain: &pusherApiDomain
+  domain: "<pusher_api_domain>"
+
 settings:
 settings:
   - FetchProviderId:
   - FetchProviderId:
       name: "<name>"
       name: "<name>"
       provider: "cz.senslog.connector.app.config.TestFetchProviderClass"
       provider: "cz.senslog.connector.app.config.TestFetchProviderClass"
+      host:
+        <<: *fetcherApiDomain
+        path: "<path>"
 
 
   - PushProviderId:
   - PushProviderId:
       name: "<name>"
       name: "<name>"
       provider: "cz.senslog.connector.app.config.TestPushProviderClass"
       provider: "cz.senslog.connector.app.config.TestPushProviderClass"
+      host:
+        <<: *pusherApiDomain
+        path: "<path>"
 
 
 connectors:
 connectors:
   - ConnectorName:
   - ConnectorName:

+ 7 - 0
connector-common/pom.xml

@@ -47,6 +47,13 @@
             <version>1.11.1</version>
             <version>1.11.1</version>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+
+
     </dependencies>
     </dependencies>
     <build>
     <build>
         <plugins>
         <plugins>

+ 11 - 11
connector-common/src/main/java/cz/senslog/connector/config/model/ConnectorDescriptor.java

@@ -16,10 +16,10 @@ public class ConnectorDescriptor {
     private final String name;
     private final String name;
 
 
     /** Class of a fetcher. */
     /** Class of a fetcher. */
-    private final Class<?> fetcher;
+    private final String fetcherId;
 
 
     /** class of a pusher. */
     /** class of a pusher. */
-    private final Class<?> pusher;
+    private final String pusherId;
 
 
     /** Period for scheduling. */
     /** Period for scheduling. */
     private final Integer period;
     private final Integer period;
@@ -30,15 +30,15 @@ public class ConnectorDescriptor {
     /**
     /**
      * Constructor sets all attributes.
      * Constructor sets all attributes.
      * @param name - name of a connector.
      * @param name - name of a connector.
-     * @param fetcher - class of a fetcher.
-     * @param pusher - class of a pusher.
+     * @param fetcherId - class of a fetcher.
+     * @param pusherId - class of a pusher.
      * @param period - period for scheduling.
      * @param period - period for scheduling.
      * @param delay - initialization delay for scheduling.
      * @param delay - initialization delay for scheduling.
      */
      */
-    public ConnectorDescriptor(String name, Class<?> fetcher, Class<?> pusher, Integer period, Integer delay) {
+    public ConnectorDescriptor(String name, String fetcherId, String pusherId, Integer period, Integer delay) {
         this.name = name;
         this.name = name;
-        this.fetcher = fetcher;
-        this.pusher = pusher;
+        this.fetcherId = fetcherId;
+        this.pusherId = pusherId;
         this.period = period;
         this.period = period;
         this.delay = delay;
         this.delay = delay;
     }
     }
@@ -47,12 +47,12 @@ public class ConnectorDescriptor {
         return name;
         return name;
     }
     }
 
 
-    public Class<?> getFetcher() {
-        return fetcher;
+    public String getFetcherId() {
+        return fetcherId;
     }
     }
 
 
-    public Class<?> getPusher() {
-        return pusher;
+    public String getPusherId() {
+        return pusherId;
     }
     }
 
 
     public Integer getPeriod() {
     public Integer getPeriod() {

+ 14 - 5
connector-common/src/main/java/cz/senslog/connector/config/model/PropertyConfig.java

@@ -26,7 +26,7 @@ import static java.util.Optional.ofNullable;
 public class PropertyConfig {
 public class PropertyConfig {
 
 
     /** Path delimiter separates nodes. */
     /** Path delimiter separates nodes. */
-    private static final String PATH_DELIMITER = "/";
+    private static final String PATH_DELIMITER = ".";
 
 
     /** Identifier of path. */
     /** Identifier of path. */
     private final String id;
     private final String id;
@@ -48,8 +48,9 @@ public class PropertyConfig {
      * @param name - name of new property.
      * @param name - name of new property.
      * @param value - value of new property.
      * @param value - value of new property.
      */
      */
-    public void setProperty(String name, Object value) {
-        properties.put(name, value);
+    public boolean setProperty(String name, Object value) {
+        Object res = properties.put(name, value);
+        return res == value;
     }
     }
 
 
     /**
     /**
@@ -68,6 +69,15 @@ public class PropertyConfig {
     }
     }
 
 
     /**
     /**
+     * Checks if property key is presents in properties.
+     * @param name - name of property
+     * @return boolean
+     */
+    public boolean containsProperty(String name) {
+        return properties.containsKey(name);
+    }
+
+    /**
      * Returns optional value. It could be anything.
      * Returns optional value. It could be anything.
      * @param name - name of property.
      * @param name - name of property.
      * @return optional object
      * @return optional object
@@ -100,8 +110,7 @@ public class PropertyConfig {
      * @return localDateTime value.
      * @return localDateTime value.
      */
      */
     public LocalDateTime getLocalDateTimeProperty(String name) {
     public LocalDateTime getLocalDateTimeProperty(String name) {
-        Object property = getProperty(name);
-        LocalDateTime value = LocalDateTimeUtils.valueOf(property);
+        LocalDateTime value = LocalDateTimeUtils.valueOf(getProperty(name));
 
 
         if (value != null) {
         if (value != null) {
             return value;
             return value;

+ 8 - 0
connector-common/src/main/java/cz/senslog/connector/exception/ModuleInterruptedException.java

@@ -0,0 +1,8 @@
+package cz.senslog.connector.exception;
+
+public class ModuleInterruptedException extends RuntimeException{
+
+    public ModuleInterruptedException(String message) {
+        super(message);
+    }
+}

+ 8 - 0
connector-common/src/main/java/cz/senslog/connector/exception/ParseException.java

@@ -0,0 +1,8 @@
+package cz.senslog.connector.exception;
+
+public class ParseException extends RuntimeException {
+
+    public ParseException(String message) {
+        super(message);
+    }
+}

+ 57 - 0
connector-common/src/main/java/cz/senslog/connector/interceptor/AbstractMethodInterceptor.java

@@ -0,0 +1,57 @@
+package cz.senslog.connector.interceptor;
+
+import cz.senslog.connector.util.Arrays;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.BiFunction;
+
+
+public abstract class AbstractMethodInterceptor implements MethodInterceptor {
+
+    private final Object object;
+    private final Method expectedMethod;
+
+    protected AbstractMethodInterceptor(Object object, Method expectedMethod) {
+        this.object = object;
+        this.expectedMethod = expectedMethod;
+    }
+
+    private static boolean methodEquals(Method a, Method b) {
+        if (a==null||b==null) { return false; }
+        if (!a.getName().equals(b.getName())) { return false; }
+
+        Class<?> aRetType = a.getReturnType(), bRetType = b.getReturnType();
+        if (aRetType==null||bRetType==null) { return false; }
+        if (!(aRetType == bRetType || bRetType.isInstance(aRetType))) { return false;}
+
+        BiFunction<Object, Object, Boolean> compareFnc = (o1, o2) -> o1.getClass().isInstance(o2.getClass());
+        return Arrays.equals(a.getParameterTypes(), b.getParameterTypes(), compareFnc);
+    }
+
+    protected Object getObject() {
+        return object;
+    }
+
+    public abstract Object[] preProcessing(Object[] methodParams);
+
+    public abstract Object postProcessing(Object returnObject);
+
+    @Override
+    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws InvocationTargetException, IllegalAccessException {
+        Object obj = object != null ? object : o;
+        if (methodEquals(expectedMethod, method)) {
+            Object[] newArgs = preProcessing(args);
+            if (newArgs != null) {
+                Object result = method.invoke(obj, newArgs);
+                return postProcessing(result);
+            } else {
+                return postProcessing(null);
+            }
+        } else {
+            return method.invoke(obj, args);
+        }
+    }
+}

+ 23 - 0
connector-common/src/main/java/cz/senslog/connector/interceptor/ProxyUtils.java

@@ -0,0 +1,23 @@
+package cz.senslog.connector.interceptor;
+
+import net.sf.cglib.proxy.Enhancer;
+
+/**
+ * Util class allows to make easier mock instance from real instance or class.
+ *
+ * @author Lukas Cerny
+ * @version 1.0
+ * @since 1.8
+ */
+
+public final class ProxyUtils {
+
+    /**
+     * Create instance of utils class is prohibited.
+     */
+    private ProxyUtils(){}
+
+    public static <A> A createProxy(Class<A> aClass, AbstractMethodInterceptor interceptor) {
+        return  (A)Enhancer.create(aClass, interceptor);
+    }
+}

+ 11 - 14
connector-common/src/main/java/cz/senslog/connector/json/BasicJson.java

@@ -3,7 +3,9 @@ package cz.senslog.connector.json;
 import com.google.gson.*;
 import com.google.gson.*;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonToken;
+import cz.senslog.connector.exception.ParseException;
 import cz.senslog.connector.exception.SyntaxException;
 import cz.senslog.connector.exception.SyntaxException;
+import cz.senslog.connector.util.Tuple;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringReader;
@@ -129,27 +131,22 @@ public class BasicJson {
         }
         }
     }
     }
 
 
-    /*
-    public static <T, F> String objectToJson(T object, Formatter<?>... formatters) {
-
+    @SafeVarargs
+    public static <R, E> R jsonToObject(String json, Type type, Tuple<Class<E>, FormatFunction<E>>... formatters) {
         GsonBuilder gsonBuilder = new GsonBuilder();
         GsonBuilder gsonBuilder = new GsonBuilder();
-        for (Formatter<?> formatter : formatters) {
-
-            // TODO does not work -> get parametrized type of the formatter class
-            ParameterizedType converterTypes = (ParameterizedType) formatter.getClass().getGenericInterfaces()[0];
-            Class formatterClass = (Class) converterTypes.getActualTypeArguments()[0];
-
-            gsonBuilder.registerTypeAdapter(LocalDateTime.class, (JsonSerializer<?>) (f, type, jsonSerializationContext) ->
-                    new JsonPrimitive(formatter.format(f)));
+        for (Tuple<Class<E>, FormatFunction<E>> formatter : formatters) {
+            gsonBuilder.registerTypeAdapter(formatter.getItem1(), new BasicJsonDeserializer<>(formatter.getItem2()));
         }
         }
-
         try {
         try {
-            return gsonBuilder.create().toJson(object);
+            Gson gson = gsonBuilder.create();
+            return gson.fromJson(json, type);
         } catch (JsonSyntaxException e) {
         } catch (JsonSyntaxException e) {
             throw new SyntaxException(e.getMessage());
             throw new SyntaxException(e.getMessage());
+        } catch (RuntimeException e) {
+            throw new ParseException(e.getMessage());
         }
         }
     }
     }
-    */
+
 
 
     /**
     /**
      * Checks if input string is in json format.
      * Checks if input string is in json format.

+ 22 - 0
connector-common/src/main/java/cz/senslog/connector/json/BasicJsonDeserializer.java

@@ -0,0 +1,22 @@
+package cz.senslog.connector.json;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+public class BasicJsonDeserializer<T> implements JsonDeserializer<T> {
+
+    private final FormatFunction<T> formatter;
+
+    public BasicJsonDeserializer(FormatFunction<T> formatter) {
+        this.formatter = formatter;
+    }
+
+    @Override
+    public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+        return formatter.apply(jsonElement.getAsString());
+    }
+}

+ 6 - 0
connector-common/src/main/java/cz/senslog/connector/json/FormatFunction.java

@@ -0,0 +1,6 @@
+package cz.senslog.connector.json;
+
+@FunctionalInterface
+public interface FormatFunction<T> {
+    T apply(String element);
+}

+ 23 - 0
connector-common/src/main/java/cz/senslog/connector/util/Arrays.java

@@ -0,0 +1,23 @@
+package cz.senslog.connector.util;
+
+import java.util.function.BiFunction;
+
+public class Arrays {
+
+    public static boolean equals(Object[] a, Object[] b, BiFunction<Object, Object, Boolean> compareTo) {
+        if (a==b) { return true; }
+        if (a==null || b==null) { return false; }
+
+        int length = a.length;
+        if (b.length != length) { return false; }
+
+        for (int i = 0; i < length; i++) {
+            Object o1 = a[i];
+            Object o2 = b[i];
+            if (!(o1==null ? o2==null : compareTo.apply(o1, o2))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 1 - 0
connector-common/src/main/java/cz/senslog/connector/util/ClassUtils.java

@@ -21,6 +21,7 @@ public class ClassUtils {
      * @return casted value.
      * @return casted value.
      */
      */
     public static <T> T cast(Object value, Class<T> castClass) {
     public static <T> T cast(Object value, Class<T> castClass) {
+        if (value == null) { return null; }
         if (value.getClass().equals(castClass) ) {
         if (value.getClass().equals(castClass) ) {
             return (T) value;
             return (T) value;
         } else {
         } else {

+ 14 - 0
connector-common/src/main/java/cz/senslog/connector/util/MethodExplorer.java

@@ -0,0 +1,14 @@
+package cz.senslog.connector.util;
+
+import java.lang.reflect.Method;
+
+public final class MethodExplorer {
+
+    public  static Method loadMethod(Class<?> aClass, String name, Class<?>... parameterTypes) {
+        try {
+            return aClass.getMethod(name, parameterTypes);
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+}

+ 2 - 2
connector-common/src/main/java/cz/senslog/connector/util/Tuple.java

@@ -27,8 +27,8 @@ public class Tuple<A, B> {
      * @param <B> type of the second value.
      * @param <B> type of the second value.
      * @return new instance of {@code Tuple}.
      * @return new instance of {@code Tuple}.
      */
      */
-    public static <A, B> Tuple of(A item1, B item2) {
-        return new Tuple<>(item1, item2);
+    public static <A, B> Tuple<A, B> of(A item1, B item2) {
+        return new Tuple<A, B>(item1, item2);
     }
     }
 
 
     /**
     /**

+ 3 - 3
connector-common/src/test/java/cz/senslog/connector/config/model/ConnectorDescriptorTest.java

@@ -10,12 +10,12 @@ class ConnectorDescriptorTest {
     @Test
     @Test
     void toString_ConvertJson_True() {
     void toString_ConvertJson_True() {
 
 
-        String jsonDescriptor = new ConnectorDescriptor("test", ConnectorDescriptorTest.class, ConnectorDescriptorTest.class, 42, 2).toString();
+        String jsonDescriptor = new ConnectorDescriptor("test", "FetcherId", "PusherId", 42, 2).toString();
         ConnectorDescriptor descriptor = jsonToObject(jsonDescriptor, ConnectorDescriptor.class);
         ConnectorDescriptor descriptor = jsonToObject(jsonDescriptor, ConnectorDescriptor.class);
 
 
         assertEquals("test", descriptor.getName());
         assertEquals("test", descriptor.getName());
-        assertEquals(ConnectorDescriptorTest.class, descriptor.getFetcher());
-        assertEquals(ConnectorDescriptorTest.class, descriptor.getPusher());
+        assertEquals("FetcherId", descriptor.getFetcherId());
+        assertEquals("PusherId", descriptor.getPusherId());
         assertEquals(42, descriptor.getPeriod());
         assertEquals(42, descriptor.getPeriod());
         assertEquals(2, descriptor.getDelay());
         assertEquals(2, descriptor.getDelay());
     }
     }

+ 1 - 1
connector-common/src/test/java/cz/senslog/connector/config/model/PropertyConfigTest.java

@@ -42,7 +42,7 @@ class PropertyConfigTest {
 
 
         PropertyConfig valuesConfig = config.getPropertyConfig("values");
         PropertyConfig valuesConfig = config.getPropertyConfig("values");
 
 
-        assertEquals("test/values", valuesConfig.getId());
+        assertEquals("test.values", valuesConfig.getId());
         assertEquals(42, valuesConfig.getIntegerProperty("integer"));
         assertEquals(42, valuesConfig.getIntegerProperty("integer"));
     }
     }
 
 

+ 4 - 4
connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetchProvider.java

@@ -12,10 +12,10 @@ import cz.senslog.connector.config.model.DefaultConfig;
 public interface ConnectorFetchProvider {
 public interface ConnectorFetchProvider {
 
 
     /**
     /**
-     * Creates a new instance of {@link ConnectorFetcher}. This method receive default
-     * configuration {@link DefaultConfig} which is used to configure the new instance of fetcher.
-     * @param config - default configuration.
+     * Creates a new instance of {@link ExecutableFetcher}. This method receive default
+     * configuration {@link DefaultConfig} which is used to configure the new instance of a fetcher executor.
+     * @param defaultConfig - default configuration.
      * @return new instance of {@link ConnectorFetcher}.
      * @return new instance of {@link ConnectorFetcher}.
      */
      */
-    ConnectorFetcher createFetcher(DefaultConfig config);
+    ExecutableFetcher createExecutableFetcher(DefaultConfig defaultConfig);
 }
 }

+ 5 - 2
connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetcher.java

@@ -1,6 +1,9 @@
 package cz.senslog.connector.fetch.api;
 package cz.senslog.connector.fetch.api;
 
 
 import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.AbstractModel;
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.util.Optional;
 
 
 /**
 /**
  * The interface {@code ConnectorFetcher} provides a generic communication interface for fetchers.
  * The interface {@code ConnectorFetcher} provides a generic communication interface for fetchers.
@@ -11,7 +14,7 @@ import cz.senslog.connector.model.api.AbstractModel;
  * @version 1.0
  * @version 1.0
  * @since 1.0
  * @since 1.0
  */
  */
-public interface ConnectorFetcher<T extends AbstractModel> {
+public interface ConnectorFetcher<S extends ProxySessionModel, T extends AbstractModel> {
 
 
     /**
     /**
      * Initialization of fetcher. Method is called only once when is created a new connector.
      * Initialization of fetcher. Method is called only once when is created a new connector.
@@ -23,5 +26,5 @@ public interface ConnectorFetcher<T extends AbstractModel> {
      * Method is periodically scheduled and contains logic of fetcher.
      * Method is periodically scheduled and contains logic of fetcher.
      * @return model of data which was fetched.
      * @return model of data which was fetched.
      */
      */
-    T fetch();
+    T fetch(Optional<S> session);
 }
 }

+ 48 - 0
connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ExecutableFetcher.java

@@ -0,0 +1,48 @@
+package cz.senslog.connector.fetch.api;
+
+import cz.senslog.connector.model.api.AbstractModel;
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.util.Optional;
+
+import static cz.senslog.connector.interceptor.ProxyUtils.createProxy;
+
+public class ExecutableFetcher<T extends AbstractModel> {
+
+    private final ConnectorFetcher<? extends ProxySessionModel, T> rawFetcher;
+
+    private final ConnectorFetcher<? extends ProxySessionModel, T> fetcher;
+
+    public static <S extends ProxySessionModel, M extends AbstractModel> ExecutableFetcher<M> create(
+            ConnectorFetcher<S, M> rawFetcher
+    ) {
+        return new ExecutableFetcher<M>(rawFetcher, rawFetcher);
+    }
+
+    public static <S extends ProxySessionModel, M extends AbstractModel> ExecutableFetcher<M> createWithProxySession(
+            FetchProxySession<S, M> proxySession
+    ) {
+        ConnectorFetcher<S, M> object = proxySession.getObject();
+        return new ExecutableFetcher<M>(object, createProxy(object.getClass(), proxySession));
+    }
+
+    private ExecutableFetcher(
+            ConnectorFetcher<? extends ProxySessionModel, T> rawFetcher,
+            ConnectorFetcher<? extends ProxySessionModel, T> fetcher
+    ) {
+        this.rawFetcher = rawFetcher;
+        this.fetcher = fetcher;
+    }
+
+    public T execute() {
+        return fetcher.fetch(Optional.empty());
+    }
+
+    public ConnectorFetcher<? extends ProxySessionModel, T> getRawFetcher() {
+        return rawFetcher;
+    }
+
+    public ConnectorFetcher<? extends ProxySessionModel, T> getFetcher() {
+        return fetcher;
+    }
+}

+ 47 - 0
connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/FetchProxySession.java

@@ -0,0 +1,47 @@
+package cz.senslog.connector.fetch.api;
+
+import cz.senslog.connector.interceptor.AbstractMethodInterceptor;
+import cz.senslog.connector.model.api.AbstractModel;
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.util.Optional;
+
+import static cz.senslog.connector.util.MethodExplorer.loadMethod;
+import static java.util.Optional.*;
+
+public abstract class FetchProxySession<S extends ProxySessionModel, T extends AbstractModel> extends AbstractMethodInterceptor {
+
+    private Optional<S> optionalSession;
+
+    protected FetchProxySession(ConnectorFetcher<S, T> fetcher) {
+        super(fetcher, loadMethod(ConnectorFetcher.class, "fetch", Optional.class));
+        this.optionalSession = Optional.empty();
+    }
+
+    protected abstract S preProcessing(Optional<S> previousSession);
+
+    protected abstract void postProcessing(Optional<T> model, Optional<S> session);
+
+    protected ConnectorFetcher<S, T> getObject() {
+        return (ConnectorFetcher<S, T>) super.getObject();
+    }
+
+    @Override
+    public Object[] preProcessing(Object[] methodParams) {
+        S s = preProcessing(optionalSession);
+        if (s.isActive()) {
+            optionalSession = of(s);
+            return new Object[]{optionalSession};
+        } else {
+            optionalSession = empty();
+            return null;
+        }
+    }
+
+    @Override
+    public Object postProcessing(Object returnObject) {
+        Optional<T> returnOpt = ofNullable((T)returnObject);
+        postProcessing(returnOpt, optionalSession);
+        return returnObject;
+    }
+}

+ 7 - 6
connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/AzureConfig.java

@@ -5,7 +5,6 @@ import cz.senslog.connector.config.model.HostConfig;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthConfig;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthConfig;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
-import java.util.Optional;
 
 
 import static cz.senslog.connector.json.BasicJson.objectToJson;
 import static cz.senslog.connector.json.BasicJson.objectToJson;
 
 
@@ -18,25 +17,27 @@ import static cz.senslog.connector.json.BasicJson.objectToJson;
  */
  */
 public class AzureConfig {
 public class AzureConfig {
 
 
-    private final LocalDateTime startDate;
-    private final Optional<LocalDateTime> endDate;
     private final Integer limitPerSensor;
     private final Integer limitPerSensor;
     private final HostConfig sensorInfoHost;
     private final HostConfig sensorInfoHost;
     private final HostConfig sensorDataHost;
     private final HostConfig sensorDataHost;
     private final AzureAuthConfig authentication;
     private final AzureAuthConfig authentication;
 
 
+    private LocalDateTime startDate;
+    private LocalDateTime endDate;
+
     /**
     /**
      * Constructor sets class attributes from default input configuration class {@link DefaultConfig}.
      * Constructor sets class attributes from default input configuration class {@link DefaultConfig}.
      * From the default configuration are dynamically get properties for attributes.
      * From the default configuration are dynamically get properties for attributes.
      * @param config - default configuration
      * @param config - default configuration
      */
      */
     public AzureConfig(DefaultConfig config) {
     public AzureConfig(DefaultConfig config) {
-        this.startDate = config.getLocalDateTimeProperty("startDate");
-        this.endDate = config.getOptionalLocalDateTimeProperty("endDate");
         this.limitPerSensor = config.getIntegerProperty("limitPerSensor");
         this.limitPerSensor = config.getIntegerProperty("limitPerSensor");
         this.sensorInfoHost = new HostConfig(config.getPropertyConfig("sensorInfoHost"));
         this.sensorInfoHost = new HostConfig(config.getPropertyConfig("sensorInfoHost"));
         this.sensorDataHost = new HostConfig(config.getPropertyConfig("sensorDataHost"));
         this.sensorDataHost = new HostConfig(config.getPropertyConfig("sensorDataHost"));
         this.authentication = new AzureAuthConfig(config.getPropertyConfig("authentication"));
         this.authentication = new AzureAuthConfig(config.getPropertyConfig("authentication"));
+
+        this.startDate = config.getLocalDateTimeProperty("startDate");
+        this.endDate = config.getOptionalLocalDateTimeProperty("endDate").orElse(null);
     }
     }
 
 
     public LocalDateTime getStartDate() {
     public LocalDateTime getStartDate() {
@@ -59,7 +60,7 @@ public class AzureConfig {
         return authentication;
         return authentication;
     }
     }
 
 
-    public Optional<LocalDateTime> getEndDate() {
+    public LocalDateTime getEndDate() {
         return endDate;
         return endDate;
     }
     }
 
 

+ 11 - 7
connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/AzureFetcher.java

@@ -2,6 +2,7 @@ package cz.senslog.connector.fetch.azure;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
 import cz.senslog.connector.config.model.HostConfig;
 import cz.senslog.connector.config.model.HostConfig;
+import cz.senslog.connector.exception.ModuleInterruptedException;
 import cz.senslog.connector.exception.SyntaxException;
 import cz.senslog.connector.exception.SyntaxException;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthenticationService;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthenticationService;
@@ -10,6 +11,7 @@ import cz.senslog.connector.http.HttpRequest;
 import cz.senslog.connector.http.HttpResponse;
 import cz.senslog.connector.http.HttpResponse;
 import cz.senslog.connector.http.URLBuilder;
 import cz.senslog.connector.http.URLBuilder;
 import cz.senslog.connector.json.JsonSchema;
 import cz.senslog.connector.json.JsonSchema;
+import cz.senslog.connector.model.api.ProxySessionModel;
 import cz.senslog.connector.model.azure.AzureModel;
 import cz.senslog.connector.model.azure.AzureModel;
 import cz.senslog.connector.model.azure.SensorData;
 import cz.senslog.connector.model.azure.SensorData;
 import cz.senslog.connector.model.azure.SensorInfo;
 import cz.senslog.connector.model.azure.SensorInfo;
@@ -47,7 +49,7 @@ import static java.util.Collections.emptyList;
  * @version 1.0
  * @version 1.0
  * @since 1.0
  * @since 1.0
  */
  */
-public class AzureFetcher implements ConnectorFetcher<AzureModel> {
+public class AzureFetcher implements ConnectorFetcher<ProxySessionModel, AzureModel> {
 
 
     private static Logger logger = LogManager.getLogger(AzureFetcher.class);
     private static Logger logger = LogManager.getLogger(AzureFetcher.class);
 
 
@@ -159,7 +161,7 @@ public class AzureFetcher implements ConnectorFetcher<AzureModel> {
     }
     }
 
 
     @Override
     @Override
-    public AzureModel fetch() {
+    public AzureModel fetch(Optional<ProxySessionModel> session) {
 
 
         if (sensorInfos.isEmpty()) {
         if (sensorInfos.isEmpty()) {
             logger.error("Sensors information were not loaded. Can not get detailed information.");
             logger.error("Sensors information were not loaded. Can not get detailed information.");
@@ -178,11 +180,13 @@ public class AzureFetcher implements ConnectorFetcher<AzureModel> {
 
 
             HostConfig sensorDataHost = config.getSensorDataHost();
             HostConfig sensorDataHost = config.getSensorDataHost();
             logger.info("Creating a http request to {}.", sensorDataHost);
             logger.info("Creating a http request to {}.", sensorDataHost);
+            String fromParam = lastFetch.format(ISO_DATE_TIME);
+            String toParam = config.getEndDate() != null ? config.getEndDate().format(ISO_DATE_TIME) : "";
             HttpRequest request = HttpRequest.newBuilder()
             HttpRequest request = HttpRequest.newBuilder()
                     .url(URLBuilder.newBuilder(sensorDataHost.getDomain(), sensorDataHost.getPath())
                     .url(URLBuilder.newBuilder(sensorDataHost.getDomain(), sensorDataHost.getPath())
                             .addParam("eui", sensorInfo.getEui())
                             .addParam("eui", sensorInfo.getEui())
-                            .addParam("from", lastFetch.format(ISO_DATE_TIME))
-                            .addParam("to", config.getEndDate().map(d -> d.format(ISO_DATE_TIME)).orElse(""))
+                            .addParam("from", fromParam)
+                            .addParam("to", toParam)
                             .addParam("limit", config.getLimitPerSensor())
                             .addParam("limit", config.getLimitPerSensor())
                             .build())
                             .build())
                     .header(AUTHORIZATION, accessToken)
                     .header(AUTHORIZATION, accessToken)
@@ -241,10 +245,10 @@ public class AzureFetcher implements ConnectorFetcher<AzureModel> {
         logger.info("Fetched data from {} to {}.", from.format(ISO_DATE_TIME), to.format(ISO_DATE_TIME));
         logger.info("Fetched data from {} to {}.", from.format(ISO_DATE_TIME), to.format(ISO_DATE_TIME));
         logger.info("Total fetched {} records.", totalFetched);
         logger.info("Total fetched {} records.", totalFetched);
 
 
-        if (totalFetched == 0 && config.getEndDate().isPresent()) {
-            throw new RuntimeException(format(
+        if (totalFetched == 0 && config.getEndDate() != null) {
+            throw new ModuleInterruptedException(format(
                     "Fetched all records from %s to %s. The connector is going to end.",
                     "Fetched all records from %s to %s. The connector is going to end.",
-                    config.getStartDate().format(ISO_DATE_TIME), config.getEndDate().get().format(ISO_DATE_TIME)
+                    config.getStartDate().format(ISO_DATE_TIME), config.getEndDate().format(ISO_DATE_TIME)
             ));
             ));
         }
         }
 
 

+ 4 - 2
connector-fetch-azure/src/main/java/cz/senslog/connector/fetch/azure/ConnectorFetchAzureProvider.java

@@ -3,8 +3,10 @@ package cz.senslog.connector.fetch.azure;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthConfig;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthConfig;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthenticationService;
 import cz.senslog.connector.fetch.azure.auth.AzureAuthenticationService;
+import cz.senslog.connector.model.azure.AzureModel;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
 
 
@@ -24,7 +26,7 @@ public final class ConnectorFetchAzureProvider implements ConnectorFetchProvider
     private static Logger logger = LogManager.getLogger(ConnectorFetchAzureProvider.class);
     private static Logger logger = LogManager.getLogger(ConnectorFetchAzureProvider.class);
 
 
     @Override
     @Override
-    public ConnectorFetcher createFetcher(DefaultConfig config) {
+    public ExecutableFetcher<AzureModel> createExecutableFetcher(DefaultConfig config) {
         logger.info("Initialization a new fetch provider {}.", ConnectorFetchAzureProvider.class);
         logger.info("Initialization a new fetch provider {}.", ConnectorFetchAzureProvider.class);
 
 
         logger.debug("Creating a new configuration.");
         logger.debug("Creating a new configuration.");
@@ -41,6 +43,6 @@ public final class ConnectorFetchAzureProvider implements ConnectorFetchProvider
         AzureFetcher fetcher = new AzureFetcher(azureConfig, authService, newHttpClient());
         AzureFetcher fetcher = new AzureFetcher(azureConfig, authService, newHttpClient());
         logger.info("Fetcher for {} was created successfully.", AzureFetcher.class);
         logger.info("Fetcher for {} was created successfully.", AzureFetcher.class);
 
 
-        return fetcher;
+        return ExecutableFetcher.create(fetcher);
     }
     }
 }
 }

+ 5 - 4
connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/ConnectorFetchAzureProviderTest.java

@@ -2,13 +2,14 @@ package cz.senslog.connector.fetch.azure;
 
 
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
-import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.HashMap;
 
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.any;
 
 
 class ConnectorFetchAzureProviderTest {
 class ConnectorFetchAzureProviderTest {
@@ -38,9 +39,9 @@ class ConnectorFetchAzureProviderTest {
         }});
         }});
 
 
         ConnectorFetchProvider provider = new ConnectorFetchAzureProvider();
         ConnectorFetchProvider provider = new ConnectorFetchAzureProvider();
-        ConnectorFetcher fetcher = provider.createFetcher(defaultConfig);
+        ExecutableFetcher fetcher = provider.createExecutableFetcher(defaultConfig);
 
 
         assertNotNull(fetcher);
         assertNotNull(fetcher);
-        assertEquals(AzureFetcher.class, fetcher.getClass());
+        assertEquals(AzureFetcher.class, fetcher.getRawFetcher().getClass());
     }
     }
 }
 }

+ 20 - 5
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/ConnectorFetchFieldClimateProvider.java

@@ -1,13 +1,14 @@
 package cz.senslog.connector.fetch.fieldclimate;
 package cz.senslog.connector.fetch.fieldclimate;
 
 
 import cz.senslog.connector.config.model.DefaultConfig;
 import cz.senslog.connector.config.model.DefaultConfig;
-import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
-import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.*;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthConfig;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthConfig;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthenticationService;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthenticationService;
+import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
 
 
+import static cz.senslog.connector.http.HttpClient.newHttpClient;
 import static cz.senslog.connector.http.HttpClient.newHttpSSLClient;
 import static cz.senslog.connector.http.HttpClient.newHttpSSLClient;
 
 
 
 
@@ -16,7 +17,7 @@ public final class ConnectorFetchFieldClimateProvider implements ConnectorFetchP
     private static Logger logger = LogManager.getLogger(ConnectorFetchFieldClimateProvider.class);
     private static Logger logger = LogManager.getLogger(ConnectorFetchFieldClimateProvider.class);
 
 
     @Override
     @Override
-    public ConnectorFetcher createFetcher(DefaultConfig defaultConfig) {
+    public ExecutableFetcher<FieldClimateModel> createExecutableFetcher(DefaultConfig defaultConfig) {
         logger.info("Initialization a new fetch provider {}.", ConnectorFetchFieldClimateProvider.class);
         logger.info("Initialization a new fetch provider {}.", ConnectorFetchFieldClimateProvider.class);
 
 
         logger.debug("Creating a new configuration.");
         logger.debug("Creating a new configuration.");
@@ -30,9 +31,23 @@ public final class ConnectorFetchFieldClimateProvider implements ConnectorFetchP
         AuthenticationService authService = new AuthenticationService(authConfig);
         AuthenticationService authService = new AuthenticationService(authConfig);
 
 
         logger.debug("Creating a new instance of {}.", FieldClimateFetcher.class);
         logger.debug("Creating a new instance of {}.", FieldClimateFetcher.class);
-        FieldClimateFetcher fetch = new FieldClimateFetcher(config, authService, newHttpSSLClient());
+        FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, newHttpSSLClient());
         logger.info("Fetcher for {} was created successfully.", FieldClimateFetcher.class);
         logger.info("Fetcher for {} was created successfully.", FieldClimateFetcher.class);
 
 
-        return fetch;
+        logger.debug("Getting a configuration for proxy session.");
+        FieldClimateSessionProxyConfig proxyConfig = config.getSessionProxy();
+
+        ExecutableFetcher<FieldClimateModel> executor;
+        if (proxyConfig != null) {
+            logger.debug("Creating a new instance of {}.", FieldClimateProxySession.class);
+            FieldClimateProxySession proxySession = new FieldClimateProxySession(fetcher, proxyConfig, newHttpClient());
+            logger.info("Fetcher session for {} was created successfully.", FieldClimateProxySession.class);
+            executor = ExecutableFetcher.createWithProxySession(proxySession);
+        } else {
+            executor = ExecutableFetcher.create(fetcher);
+        }
+        logger.info("Fetcher executor for {} was created successfully.", FieldClimateFetcher.class);
+
+        return executor;
     }
     }
 }
 }

+ 8 - 0
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateConfig.java

@@ -14,6 +14,7 @@ public class FieldClimateConfig {
     private final HostConfig stationTimeRangeHost;
     private final HostConfig stationTimeRangeHost;
     private final AuthConfig authentication;
     private final AuthConfig authentication;
     private final Integer period;
     private final Integer period;
+    private final FieldClimateSessionProxyConfig sessionProxy;
 
 
     public FieldClimateConfig(DefaultConfig config) {
     public FieldClimateConfig(DefaultConfig config) {
         this.startDate = config.getLocalDateTimeProperty("startDate");
         this.startDate = config.getLocalDateTimeProperty("startDate");
@@ -22,6 +23,9 @@ public class FieldClimateConfig {
         this.stationTimeRangeHost = new HostConfig(config.getPropertyConfig("stationTimeRangeHost"));
         this.stationTimeRangeHost = new HostConfig(config.getPropertyConfig("stationTimeRangeHost"));
         this.authentication = new AuthConfig(config.getPropertyConfig("authentication"));
         this.authentication = new AuthConfig(config.getPropertyConfig("authentication"));
         this.period = config.getIntegerProperty("period");
         this.period = config.getIntegerProperty("period");
+
+        this.sessionProxy = config.containsProperty("sessionProxy") ?
+                new FieldClimateSessionProxyConfig(config.getPropertyConfig("sessionProxy")) : null;
     }
     }
 
 
     public LocalDateTime getStartDate() {
     public LocalDateTime getStartDate() {
@@ -47,4 +51,8 @@ public class FieldClimateConfig {
     public Integer getPeriod() {
     public Integer getPeriod() {
         return period;
         return period;
     }
     }
+
+    public FieldClimateSessionProxyConfig getSessionProxy() {
+        return sessionProxy;
+    }
 }
 }

+ 22 - 25
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcher.java

@@ -1,10 +1,8 @@
 package cz.senslog.connector.fetch.fieldclimate;
 package cz.senslog.connector.fetch.fieldclimate;
 
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializer;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
 import cz.senslog.connector.config.model.HostConfig;
 import cz.senslog.connector.config.model.HostConfig;
+import cz.senslog.connector.exception.ParseException;
 import cz.senslog.connector.exception.SyntaxException;
 import cz.senslog.connector.exception.SyntaxException;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthenticationService;
 import cz.senslog.connector.fetch.fieldclimate.auth.AuthenticationService;
@@ -12,15 +10,16 @@ import cz.senslog.connector.http.HttpClient;
 import cz.senslog.connector.http.HttpRequest;
 import cz.senslog.connector.http.HttpRequest;
 import cz.senslog.connector.http.HttpResponse;
 import cz.senslog.connector.http.HttpResponse;
 import cz.senslog.connector.http.URLBuilder;
 import cz.senslog.connector.http.URLBuilder;
+import cz.senslog.connector.model.api.ProxySessionModel;
 import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
 import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
 import cz.senslog.connector.model.fieldclimate.StationData;
 import cz.senslog.connector.model.fieldclimate.StationData;
 import cz.senslog.connector.model.fieldclimate.StationInfo;
 import cz.senslog.connector.model.fieldclimate.StationInfo;
+import cz.senslog.connector.util.Tuple;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
 
 
 import java.lang.reflect.Type;
 import java.lang.reflect.Type;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
-import java.time.format.DateTimeParseException;
 import java.util.*;
 import java.util.*;
 
 
 import static cz.senslog.connector.http.ContentType.APPLICATION_JSON;
 import static cz.senslog.connector.http.ContentType.APPLICATION_JSON;
@@ -35,27 +34,29 @@ import static java.time.format.DateTimeFormatter.ofPattern;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyList;
 import static org.apache.http.client.utils.DateUtils.formatDate;
 import static org.apache.http.client.utils.DateUtils.formatDate;
 
 
-public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel> {
+public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateSession, FieldClimateModel> {
 
 
     private static Logger logger = LogManager.getLogger(FieldClimateFetcher.class);
     private static Logger logger = LogManager.getLogger(FieldClimateFetcher.class);
 
 
-
     private final AuthenticationService authService;
     private final AuthenticationService authService;
     private final FieldClimateConfig config;
     private final FieldClimateConfig config;
     private final HttpClient httpClient;
     private final HttpClient httpClient;
 
 
     private List<StationInfo> stationInfos;
     private List<StationInfo> stationInfos;
-    private Map<String, LocalDateTime> stationTimeRanges;
-
+    private FieldClimateSession localSession;
 
 
     public FieldClimateFetcher(FieldClimateConfig config, AuthenticationService authService, HttpClient httpClient) {
     public FieldClimateFetcher(FieldClimateConfig config, AuthenticationService authService, HttpClient httpClient) {
         this.authService = authService;
         this.authService = authService;
         this.config = config;
         this.config = config;
         this.httpClient = httpClient;
         this.httpClient = httpClient;
+        this.localSession = FieldClimateSession.emptySession();
     }
     }
 
 
+    public FieldClimateFetcher() { this(null, null, null); }
+
     @Override
     @Override
     public void init() throws Exception {
     public void init() throws Exception {
+        assert config != null; assert  authService != null; assert  httpClient != null;
 
 
         String requestDate = formatDate(new Date());
         String requestDate = formatDate(new Date());
         HostConfig stationsHost = config.getStationsHost();
         HostConfig stationsHost = config.getStationsHost();
@@ -67,6 +68,7 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
                 .header(AUTHORIZATION, authService.getAccessToken(seed))
                 .header(AUTHORIZATION, authService.getAccessToken(seed))
                 .header(DATE, requestDate)
                 .header(DATE, requestDate)
                 .build();
                 .build();
+        logger.info("Creating a http request to {}.", request);
 
 
         HttpResponse response = httpClient.send(request);
         HttpResponse response = httpClient.send(request);
 
 
@@ -94,15 +96,13 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
         stationInfos = stations;
         stationInfos = stations;
         logger.info("{} stations were loaded.", stationInfos.size());
         logger.info("{} stations were loaded.", stationInfos.size());
         logger.info(stationInfos.toString());
         logger.info(stationInfos.toString());
-
-        stationTimeRanges = new HashMap<>(stations.size());
-        for (StationInfo station : stations) {
-            stationTimeRanges.put(station.getName().getOriginal(), config.getStartDate());
-        }
     }
     }
 
 
     @Override
     @Override
-    public FieldClimateModel fetch() {
+    public FieldClimateModel fetch(Optional<FieldClimateSession> persistenceSession) {
+        assert config != null; assert  authService != null; assert  httpClient != null;
+
+        FieldClimateSession session = persistenceSession.filter(ProxySessionModel::isActive).orElse(localSession);
 
 
         List<StationData> stationDataList = new ArrayList<>(stationInfos.size());
         List<StationData> stationDataList = new ArrayList<>(stationInfos.size());
         int totalFetched = 0;
         int totalFetched = 0;
@@ -120,7 +120,7 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
             }
             }
 
 
             logger.debug("Getting a start date time for station {}.", stationId);
             logger.debug("Getting a start date time for station {}.", stationId);
-            LocalDateTime localFrom = stationTimeRanges.get(stationId);
+            LocalDateTime localFrom = session.getLastTimeForUnit(stationId, config.getStartDate());
 
 
             StationTimeRange timeRange = getTimeRangeForStation(stationId);
             StationTimeRange timeRange = getTimeRangeForStation(stationId);
             if (timeRange == null) continue;
             if (timeRange == null) continue;
@@ -160,7 +160,7 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
 
 
             logger.info("Sending the http request: {}", request);
             logger.info("Sending the http request: {}", request);
             HttpResponse response = httpClient.send(request);
             HttpResponse response = httpClient.send(request);
-            logger.info("Received a response with a status: {}.", response.getStatus());
+            logger.info("Received a response with a status: {} for the domain {}.", response.getStatus(), stationDataHost.getDomain());
 
 
 
 
             if (response.isOk()) {
             if (response.isOk()) {
@@ -206,7 +206,7 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
                 }
                 }
             }
             }
 
 
-            stationTimeRanges.put(stationId, localTo);
+            session.addLastUnitTime(stationId, localTo);
 
 
             if (globalFrom.isAfter(localFrom)) {
             if (globalFrom.isAfter(localFrom)) {
                 globalFrom = localFrom;
                 globalFrom = localFrom;
@@ -260,15 +260,12 @@ public class FieldClimateFetcher implements ConnectorFetcher<FieldClimateModel>
 
 
         StationTimeRange timeRange = null;
         StationTimeRange timeRange = null;
         try {
         try {
-            logger.debug("Creating a new json converter via class {}.", Gson.class);
-            Gson gson = new GsonBuilder()
-                    .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (jsonElement, type, jsonDeserializationContext) ->
-                            LocalDateTime.parse(jsonElement.getAsString(), ofPattern("yyyy-MM-dd HH:mm:ss")))
-                    .create();
-
             logger.debug("Parsing body of the json response to the class {}.", StationTimeRange.class);
             logger.debug("Parsing body of the json response to the class {}.", StationTimeRange.class);
-            timeRange = gson.fromJson(response.getBody(), StationTimeRange.class);
-        } catch (SyntaxException | DateTimeParseException e) {
+            timeRange = jsonToObject(response.getBody(), StationTimeRange.class,
+                    Tuple.of(LocalDateTime.class,
+                            el -> LocalDateTime.parse(el, ofPattern("yyyy-MM-dd HH:mm:ss")))
+            );
+        } catch (SyntaxException | ParseException e) {
             logger.catching(e);
             logger.catching(e);
         }
         }
 
 

+ 100 - 0
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateProxySession.java

@@ -0,0 +1,100 @@
+package cz.senslog.connector.fetch.fieldclimate;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.connector.config.model.HostConfig;
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.fetch.api.FetchProxySession;
+import cz.senslog.connector.http.HttpClient;
+import cz.senslog.connector.http.HttpRequest;
+import cz.senslog.connector.http.HttpResponse;
+import cz.senslog.connector.http.URLBuilder;
+import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
+import cz.senslog.connector.util.Tuple;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import static cz.senslog.connector.fetch.fieldclimate.FieldClimateSession.emptySession;
+import static cz.senslog.connector.json.BasicJson.jsonToObject;
+import static cz.senslog.connector.model.converter.FieldClimateUnitConverter.convertIdToFieldClimateId;
+import static java.time.ZonedDateTime.parse;
+import static java.time.format.DateTimeFormatter.ofPattern;
+
+public class FieldClimateProxySession extends FetchProxySession<FieldClimateSession, FieldClimateModel> {
+
+    private static Logger logger = LogManager.getLogger(FieldClimateProxySession.class);
+
+    protected static class ObservationInfo { ZonedDateTime timeStamp; Long unitId; }
+
+    private final HttpClient httpClient;
+    private final FieldClimateSessionProxyConfig config;
+
+    public FieldClimateProxySession(
+            ConnectorFetcher<FieldClimateSession, FieldClimateModel> instance,
+            FieldClimateSessionProxyConfig config,
+            HttpClient httpClient
+    ) {
+        super(instance);
+        this.config = config;
+        this.httpClient = httpClient;
+    }
+
+    @Override
+    protected FieldClimateSession preProcessing(Optional<FieldClimateSession> previousSession) {
+
+        HostConfig host = config.getLastObservationHost();
+        logger.info("Getting last observations from {}.", host.getDomain());
+
+        HttpRequest request = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(host.getDomain(), host.getPath())
+                        .addParam("Operation", "GetLastObservations")
+                        .addParam("group", config.getGroup())
+                        .addParam("user", config.getUser())
+                        .build())
+                .build();
+        logger.info("Creating a http request to {}.", request);
+
+        HttpResponse response = httpClient.send(request);
+        logger.info("Received a response with a status: {} for the domain {}.", response.getStatus(), host.getDomain());
+
+        if (response.isError()) {
+            logger.error("Can not get data from the server {}. Error {} {}",
+                    host.getDomain(), response.getStatus(), response.getBody());
+            return emptySession();
+        }
+
+        logger.debug("Parsing body of the response to the list of class {}.", ObservationInfo.class);
+        Type lastObsType = new TypeToken<Collection<ObservationInfo>>() {}.getType();
+        List<ObservationInfo> lastObservations = jsonToObject(response.getBody(), lastObsType,
+                Tuple.of(ZonedDateTime.class, el -> parse(el+"00",  ofPattern("yyyy-MM-dd HH:mm:ssZ")))
+        );
+
+        FieldClimateSession session = previousSession.filter(FieldClimateSession::isActive)
+                .orElse(new FieldClimateSession(true));
+        logger.debug("Created a new session of {}.", FieldClimateSession.class);
+
+        logger.debug("Filling the new session of last timestamps from observations per each station.");
+        for (ObservationInfo info : lastObservations) {
+            LocalDateTime lastUnit = info.timeStamp.toLocalDateTime();
+            String fcUnitId = convertIdToFieldClimateId(info.unitId);
+            session.addLastUnitTime(fcUnitId, lastUnit);
+        }
+
+        logger.info("Loaded and successfully parsed {} last observations from stations.", lastObservations.size());
+        return session;
+    }
+
+    @Override
+    protected void postProcessing(Optional<FieldClimateModel> returnedModel, Optional<FieldClimateSession> currentSession) {
+        if (currentSession.isPresent() && currentSession.get().isActive()) {
+            FieldClimateSession session = currentSession.get();
+            // TODO persistence the current session
+        }
+    }
+}

+ 75 - 0
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSession.java

@@ -0,0 +1,75 @@
+package cz.senslog.connector.fetch.fieldclimate;
+
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+import static java.time.LocalDateTime.MIN;
+
+public class FieldClimateSession extends ProxySessionModel {
+
+    static class UnitSession {
+        boolean used;
+
+        LocalDateTime lastTime;
+
+        public UnitSession(LocalDateTime lastTime) {
+            this.used = false;
+            this.lastTime = lastTime;
+        }
+    }
+
+    private Map<String, UnitSession> unitSessions;
+
+    public static FieldClimateSession emptySession() {
+        return new FieldClimateSession(false);
+    }
+
+    public FieldClimateSession(boolean isActive) {
+        super(isActive);
+        this.unitSessions = new HashMap<>();
+    }
+
+    public void addLastUnitTime(String unitId, LocalDateTime unitTime) {
+        String id = unitId.toUpperCase();
+        UnitSession unitSession = unitSessions.get(id);
+        if (unitSession == null) {
+            unitSession = new UnitSession(unitTime);
+            unitSessions.put(id, unitSession);
+        } else if (unitTime.isAfter(unitSession.lastTime)) {
+            unitSession.lastTime = unitTime;
+        }
+    }
+
+    public LocalDateTime getLastTimeForUnit(String unitId, LocalDateTime minUnitTime) {
+        String id = unitId.toUpperCase();
+        UnitSession unitSession = this.unitSessions.get(id);
+        LocalDateTime savedLast = MIN;
+        if (unitSession != null) {
+            unitSession.used = true;
+            savedLast = unitSession.lastTime;
+        }
+        return savedLast.isBefore(minUnitTime) ? minUnitTime : savedLast;
+    }
+
+    public String[] reduceLastUnits(String... validUnits) {
+        Set<String> unitsSet = new HashSet<>(Arrays.asList(validUnits));
+        List<String> removed = new ArrayList<>();
+        Iterator<Map.Entry<String, UnitSession>> unitIterator = unitSessions.entrySet().iterator();
+        while (unitIterator.hasNext()) {
+            Map.Entry<String, UnitSession> entry = unitIterator.next();
+            if (unitsSet.contains(entry.getKey())) {
+                removed.add(entry.getKey());
+                unitIterator.remove();
+            }
+        }
+        return removed.toArray(new String[0]);
+    }
+
+    @Override
+    public String toString() {
+        boolean isEmpty = !isActive() && unitSessions.isEmpty();
+        return "FieldClimateSession." + (isEmpty ? "empty" : "active");
+    }
+}

+ 29 - 0
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSessionProxyConfig.java

@@ -0,0 +1,29 @@
+package cz.senslog.connector.fetch.fieldclimate;
+
+import cz.senslog.connector.config.model.HostConfig;
+import cz.senslog.connector.config.model.PropertyConfig;
+
+public class FieldClimateSessionProxyConfig {
+
+    private final HostConfig lastObservationHost;
+    private final String user;
+    private final String group;
+
+    public FieldClimateSessionProxyConfig(PropertyConfig config) {
+        this.lastObservationHost = new HostConfig(config.getPropertyConfig("lastObservationHost"));
+        this.user = config.getStringProperty("user");
+        this.group = config.getStringProperty("group");
+    }
+
+    public HostConfig getLastObservationHost() {
+        return lastObservationHost;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+}

+ 65 - 23
connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateFetcherTest.java

@@ -18,6 +18,7 @@ import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Optional;
 
 
 import static cz.senslog.connector.http.HttpCode.OK;
 import static cz.senslog.connector.http.HttpCode.OK;
 import static cz.senslog.connector.http.HttpCode.SERVER_ERROR;
 import static cz.senslog.connector.http.HttpCode.SERVER_ERROR;
@@ -60,6 +61,15 @@ class FieldClimateFetcherTest {
             put("privateKey", "ed2e4abacdaad1d542eeabcec4ee4f6c8fbf3b8bb167b84b");
             put("privateKey", "ed2e4abacdaad1d542eeabcec4ee4f6c8fbf3b8bb167b84b");
         }});
         }});
 
 
+        fcDConfig.setProperty("sessionProxy", new HashMap<String, Object>(){{
+            put("user", "<username>");
+            put("group", "<group>");
+            put("lastObservationHost",  new HashMap<String, String>(){{
+                put("domain", "http://127.0.0.1:9080");
+                put("path", "SensorService");
+            }});
+        }});
+
         fcDConfig.setProperty("startDate", startDate);
         fcDConfig.setProperty("startDate", startDate);
         fcDConfig.setProperty("period", 12);
         fcDConfig.setProperty("period", 12);
 
 
@@ -143,11 +153,19 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertTrue(model.getStations().isEmpty());
         assertTrue(model.getStations().isEmpty());
-        assertEquals(startDate, model.getFrom());
-        assertEquals(startDate, model.getTo());
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime expectationNow = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), now.getHour(), now.getMinute());
+
+        LocalDateTime nowFrom = model.getFrom();
+        LocalDateTime actualFrom = LocalDateTime.of(nowFrom.getYear(), nowFrom.getMonth(), nowFrom.getDayOfMonth(), nowFrom.getHour(), nowFrom.getMinute());
+        assertEquals(expectationNow, actualFrom);
+
+        LocalDateTime nowTo = model.getTo();
+        LocalDateTime actualTo = LocalDateTime.of(nowTo.getYear(), nowTo.getMonth(), nowTo.getDayOfMonth(), nowTo.getHour(), nowTo.getMinute());
+        assertEquals(expectationNow, actualTo);
     }
     }
 
 
     @Test
     @Test
@@ -176,11 +194,9 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertTrue(model.getStations().isEmpty());
         assertTrue(model.getStations().isEmpty());
-        assertEquals(startDate, model.getFrom());
-        assertEquals(startDate, model.getTo());
     }
     }
 
 
     @Test
     @Test
@@ -209,11 +225,9 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertTrue(model.getStations().isEmpty());
         assertTrue(model.getStations().isEmpty());
-        assertEquals(startDate, model.getFrom());
-        assertEquals(startDate, model.getTo());
     }
     }
 
 
     @Test
     @Test
@@ -249,11 +263,9 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertTrue(model.getStations().isEmpty());
         assertTrue(model.getStations().isEmpty());
-        assertEquals(startDate, model.getFrom());
-        assertEquals(startDate, model.getTo());
     }
     }
 
 
     @Test
     @Test
@@ -289,11 +301,9 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertTrue(model.getStations().isEmpty());
         assertTrue(model.getStations().isEmpty());
-        assertEquals(startDate, model.getFrom());
-        assertEquals(startDate, model.getTo());
     }
     }
 
 
     @Test
     @Test
@@ -348,7 +358,7 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model = fetcher.fetch();
+        FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
         assertEquals(startDate.plusHours(1), model.getFrom());
         assertEquals(startDate.plusHours(1), model.getFrom());
         assertEquals(startDate.plusHours(2), model.getTo());
         assertEquals(startDate.plusHours(2), model.getTo());
@@ -411,23 +421,55 @@ class FieldClimateFetcherTest {
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, httpClient);
         fetcher.init();
         fetcher.init();
 
 
-        FieldClimateModel model1 = fetcher.fetch();
+        FieldClimateModel model1 = fetcher.fetch(Optional.empty());
         assertEquals(startDate, model1.getFrom());
         assertEquals(startDate, model1.getFrom());
         assertEquals(startDate.plusHours(config.getPeriod()), model1.getTo());
         assertEquals(startDate.plusHours(config.getPeriod()), model1.getTo());
 
 
-        FieldClimateModel model2 = fetcher.fetch();
+        FieldClimateModel model2 = fetcher.fetch(Optional.empty());
         assertEquals(startDate.plusHours(config.getPeriod()), model2.getFrom());
         assertEquals(startDate.plusHours(config.getPeriod()), model2.getFrom());
         assertEquals(startDate.plusHours(2*config.getPeriod()), model2.getTo());
         assertEquals(startDate.plusHours(2*config.getPeriod()), model2.getTo());
 
 
-        FieldClimateModel model3 = fetcher.fetch();
-        assertEquals(startDate.plusHours(2*config.getPeriod()), model3.getFrom());
-        assertEquals(startDate.plusHours(2*config.getPeriod()), model3.getTo());
+        FieldClimateModel model3 = fetcher.fetch(Optional.empty());
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime expectationNow = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), now.getHour(), now.getMinute());
 
 
+        LocalDateTime nowFrom = model3.getFrom();
+        LocalDateTime actualFrom = LocalDateTime.of(nowFrom.getYear(), nowFrom.getMonth(), nowFrom.getDayOfMonth(), nowFrom.getHour(), nowFrom.getMinute());
+        assertEquals(expectationNow, actualFrom);
+
+        LocalDateTime nowTo = model3.getTo();
+        LocalDateTime actualTo = LocalDateTime.of(nowTo.getYear(), nowTo.getMonth(), nowTo.getDayOfMonth(), nowTo.getHour(), nowTo.getMinute());
+        assertEquals(expectationNow, actualTo);
     }
     }
 
 
 
 
     /*
     /*
     @Test
     @Test
+    void proxyFetch() throws Exception {
+
+        DefaultConfig propertyConfig = new DefaultConfig("proxySession", null);
+        propertyConfig.setProperty("lastObservationHost", new HashMap<String, String>(){{
+            put("domain", "http://51.15.45.95:8080/senslog1");
+            put("path", "SensorService");
+        }});
+        propertyConfig.setProperty("user", "vilcini");
+        propertyConfig.setProperty("group", "vilcini");
+
+        FieldClimateSessionProxyConfig sessionProxyConfig = new FieldClimateSessionProxyConfig(propertyConfig);
+
+        FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, newHttpSSLClient());
+        FieldClimateProxySession proxySession = new FieldClimateProxySession(fetcher, sessionProxyConfig, newHttpClient());
+
+        ExecutableFetcher<FieldClimateModel> executableFetcher = ExecutableFetcher.createWithProxySession(proxySession);
+        executableFetcher.getRawFetcher().init();
+
+        executableFetcher.execute();
+        executableFetcher.execute();
+    }
+    */
+
+    /*
+    @Test
     void fetch() throws Exception {
     void fetch() throws Exception {
 
 
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, HttpClient.newHttpSSLClient());
         FieldClimateFetcher fetcher = new FieldClimateFetcher(config, authService, HttpClient.newHttpSSLClient());
@@ -435,7 +477,7 @@ class FieldClimateFetcherTest {
         fetcher.init();
         fetcher.init();
 
 
         for (int i = 0; i < 10; i++) {
         for (int i = 0; i < 10; i++) {
-            FieldClimateModel model = fetcher.fetch();
+            FieldClimateModel model = fetcher.fetch(Optional.empty());
 
 
             model.getStations().removeIf(s -> !s.getId().equals("0120821E"));
             model.getStations().removeIf(s -> !s.getId().equals("0120821E"));
             model.getStations().forEach(s -> s.getSensors().removeIf(se -> se.getCode() != 19969));
             model.getStations().forEach(s -> s.getSensors().removeIf(se -> se.getCode() != 19969));
@@ -450,7 +492,7 @@ class FieldClimateFetcherTest {
         }
         }
 
 
 
 
-        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fetcher.fetch());
+        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fetcher.fetch(Optional.empty()));
         Observation obs = (Observation)model.getObservations().get(0);
         Observation obs = (Observation)model.getObservations().get(0);
 
 
         System.out.println(obs);
         System.out.println(obs);

+ 127 - 0
connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateProxySessionTest.java

@@ -0,0 +1,127 @@
+package cz.senslog.connector.fetch.fieldclimate;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializer;
+import cz.senslog.connector.config.model.DefaultConfig;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.http.HttpClient;
+import cz.senslog.connector.http.HttpRequest;
+import cz.senslog.connector.http.HttpResponse;
+import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
+import cz.senslog.connector.model.fieldclimate.StationData;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.stubbing.Answer;
+
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Optional;
+
+import static cz.senslog.connector.http.HttpCode.OK;
+import static cz.senslog.connector.http.HttpCode.SERVER_ERROR;
+import static cz.senslog.connector.model.converter.FieldClimateUnitConverter.convertIdToFieldClimateId;
+import static java.time.ZoneOffset.UTC;
+import static java.time.format.DateTimeFormatter.ofPattern;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class FieldClimateProxySessionTest {
+
+    @BeforeEach
+    void setUp() {
+
+    }
+
+    @Test
+    void preProcessing_serverError_nullModel() throws Exception {
+
+        DefaultConfig propertyConfig = new DefaultConfig("proxySession", null);
+        propertyConfig.setProperty("lastObservationHost", new HashMap<String, String>(){{
+            put("domain", "http://127.0.0.1");
+            put("path", "SensorService");
+        }});
+        propertyConfig.setProperty("user", "<user>");
+        propertyConfig.setProperty("group", "<group>");
+
+        HttpClient httpClient = mock(HttpClient.class);
+        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock ->
+                HttpResponse.newBuilder()
+                    .status(SERVER_ERROR).body("")
+                    .build()
+        );
+
+
+        FieldClimateSessionProxyConfig sessionProxyConfig = new FieldClimateSessionProxyConfig(propertyConfig);
+        FieldClimateFetcher fetcher = mock(FieldClimateFetcher.class);
+        FieldClimateProxySession proxy = new FieldClimateProxySession(fetcher, sessionProxyConfig, httpClient);
+        ExecutableFetcher<FieldClimateModel> executableFetcher = ExecutableFetcher.createWithProxySession(proxy);
+
+        FieldClimateModel model = executableFetcher.execute();
+
+        assertNull(model);
+    }
+
+    @Test
+    void preProcessing_true() throws Exception {
+
+        DefaultConfig propertyConfig = new DefaultConfig("proxySession", null);
+        propertyConfig.setProperty("lastObservationHost", new HashMap<String, String>(){{
+            put("domain", "http://127.0.0.1");
+            put("path", "SensorService");
+        }});
+        propertyConfig.setProperty("user", "<user>");
+        propertyConfig.setProperty("group", "<group>");
+
+        ZonedDateTime timestamp = ZonedDateTime.of(2020, 1, 1, 1, 1, 0, 0, UTC);
+        Long unitId = 1234L;
+        String stationId = convertIdToFieldClimateId(unitId);
+
+        HttpClient httpClient = mock(HttpClient.class);
+        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
+            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
+            String path = request.getUrl().getPath();
+            if (path.contains("SensorService")) {
+                FieldClimateProxySession.ObservationInfo lastObs = new FieldClimateProxySession.ObservationInfo();
+                lastObs.timeStamp = timestamp;
+                lastObs.unitId = unitId;
+
+                Gson gson = new Gson().newBuilder()
+                        .registerTypeAdapter(ZonedDateTime.class, (JsonSerializer<ZonedDateTime>) (zonedDateTime, type, jsonSerializationContext) -> {
+                                String dateTime = zonedDateTime.format(ofPattern("yyyy-MM-dd HH:mm:ssZ"));
+                                return new JsonPrimitive(dateTime.substring(0, dateTime.length()-2));
+                        }).create();
+                String stationsJson = gson.toJson(singletonList(lastObs));
+                return HttpResponse.newBuilder()
+                        .status(OK).body(stationsJson).build();
+            }
+            return HttpResponse.newBuilder()
+                    .status(SERVER_ERROR).body("")
+                    .build();
+        });
+
+        FieldClimateFetcher fetcher = mock(FieldClimateFetcher.class);
+        when(fetcher.fetch(any(Optional.class))).then(a -> {
+            FieldClimateSession session = (FieldClimateSession) a.getArgument(0, Optional.class).get();
+            LocalDateTime dateTime = session.getLastTimeForUnit(stationId, LocalDateTime.MIN);
+            StationData stationData = new StationData();
+            stationData.setId(stationId);
+            return new FieldClimateModel(singletonList(stationData), dateTime, dateTime);
+        });
+
+        FieldClimateSessionProxyConfig sessionProxyConfig = new FieldClimateSessionProxyConfig(propertyConfig);
+        FieldClimateProxySession proxySession = new FieldClimateProxySession(fetcher, sessionProxyConfig, httpClient);
+        ExecutableFetcher<FieldClimateModel> executableFetcher = ExecutableFetcher.createWithProxySession(proxySession);
+
+        FieldClimateModel model = executableFetcher.execute();
+
+        assertEquals(1, model.getStations().size());
+        assertEquals(stationId, model.getStations().get(0).getId());
+        assertEquals(timestamp.toLocalDateTime(), model.getFrom());
+    }
+}

+ 67 - 0
connector-fetch-fieldclimate/src/test/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSessionTest.java

@@ -0,0 +1,67 @@
+package cz.senslog.connector.fetch.fieldclimate;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+
+import static java.time.LocalDateTime.MIN;
+import static org.junit.jupiter.api.Assertions.*;
+
+class FieldClimateSessionTest {
+
+    @BeforeEach
+    void setUp() {
+    }
+
+    @Test
+    void isActive_emptySession_false() {
+        FieldClimateSession session = FieldClimateSession.emptySession();
+        assertFalse(session.isActive());
+    }
+
+    @Test
+    void isActive_newSession_true() {
+        FieldClimateSession session = new FieldClimateSession(true);
+        assertTrue(session.isActive());
+    }
+
+    @Test
+    void lastUnitTime_addNewUnit() {
+        FieldClimateSession session = new FieldClimateSession(true);
+
+        LocalDateTime actualTime = LocalDateTime.now();
+
+        session.addLastUnitTime("#1", actualTime);
+        session.addLastUnitTime("#1", actualTime.plusMinutes(1));
+        session.addLastUnitTime("#1", actualTime.minusMinutes(1));
+
+        session.addLastUnitTime("#2", actualTime);
+        session.addLastUnitTime("#2", actualTime.plusMinutes(2));
+        session.addLastUnitTime("#2", actualTime.minusMinutes(2));
+
+        assertEquals(actualTime.plusMinutes(1), session.getLastTimeForUnit("#1", MIN));
+        assertEquals(actualTime.plusMinutes(2), session.getLastTimeForUnit("#2", MIN));
+    }
+
+    @Test
+    void reduceLastUnits_true() {
+
+        FieldClimateSession session = new FieldClimateSession(true);
+
+        LocalDateTime actualTime = LocalDateTime.now();
+
+        session.addLastUnitTime("#1", actualTime);
+        session.addLastUnitTime("#2", actualTime);
+        session.addLastUnitTime("#3", actualTime);
+        session.addLastUnitTime("#4", actualTime);
+
+        String[] removedUnits = session.reduceLastUnits("#1", "#2");
+        assertEquals(2, removedUnits.length);
+
+        assertEquals(actualTime, session.getLastTimeForUnit("#3", MIN));
+        assertEquals(actualTime, session.getLastTimeForUnit("#4", MIN));
+        assertEquals(MIN, session.getLastTimeForUnit("#1", MIN));
+        assertEquals(MIN, session.getLastTimeForUnit("#2", MIN));
+    }
+}

+ 14 - 0
connector-model/src/main/java/cz/senslog/connector/model/api/ProxySessionModel.java

@@ -0,0 +1,14 @@
+package cz.senslog.connector.model.api;
+
+public abstract class ProxySessionModel {
+
+    private final boolean isActive;
+
+    public ProxySessionModel(boolean isActive) {
+        this.isActive = isActive;
+    }
+
+    public boolean isActive() {
+        return isActive;
+    }
+}

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

@@ -30,19 +30,6 @@ public final class AzureModelSenslogV1ModelConverter implements Converter<AzureM
 
 
     private static Logger logger = LogManager.getLogger(AzureModelSenslogV1ModelConverter.class);
     private static Logger logger = LogManager.getLogger(AzureModelSenslogV1ModelConverter.class);
 
 
-    /**
-     * Converter sensor EUI in hex value to sensor ID in dec value.
-     * @param hexValue - identifier in hex.
-     * @return identifier in dec.
-     */
-    private static Long convertEuiHexToUnitIdDec(String hexValue) {
-        switch (hexValue) {
-            case "8CF9574000000948": return 10002376L;
-            case "8CF95740000008AE": return 10002222L;
-            default: return null;
-        }
-    }
-
     @Override
     @Override
     public SenslogV1Model convert(AzureModel inputModel) {
     public SenslogV1Model convert(AzureModel inputModel) {
 
 
@@ -66,7 +53,7 @@ public final class AzureModelSenslogV1ModelConverter implements Converter<AzureM
         for (SensorInfo sensorInfo : sensorInfos) {
         for (SensorInfo sensorInfo : sensorInfos) {
             logger.debug("Converting sensor with Eui {}.", sensorInfo.getEui());
             logger.debug("Converting sensor with Eui {}.", sensorInfo.getEui());
 
 
-            Long unitId = convertEuiHexToUnitIdDec(sensorInfo.getEui());
+            Long unitId = AzureUnitConverter.convertIdToSensLogId(sensorInfo.getEui());
             if (unitId == null) {
             if (unitId == null) {
                 logger.error("EUI converter for '{}' does not exist.", sensorInfo.getEui());
                 logger.error("EUI converter for '{}' does not exist.", sensorInfo.getEui());
                 continue;
                 continue;

+ 44 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/AzureUnitConverter.java

@@ -0,0 +1,44 @@
+package cz.senslog.connector.model.converter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Converter sensor EUI in hex value to sensor ID in dec value.
+ * @param hexValue - identifier in hex.
+ * @return identifier in dec.
+ */
+public class AzureUnitConverter {
+
+    private static Map<String, Long> CONVERT_TABLE;
+
+    static {
+        CONVERT_TABLE = new HashMap<>();
+
+        CONVERT_TABLE.put("8CF9574000000948", 10002376L);
+        CONVERT_TABLE.put("8CF95740000008AE", 10002222L);
+    }
+
+    private static Long getIdForType(String azureID) {
+        if (azureID == null) return null;
+        return CONVERT_TABLE.getOrDefault(azureID, null);
+    }
+
+    private static String getAzureId(Long id) {
+        if (id == null) return null;
+        for (Map.Entry<String, Long> idEntry : CONVERT_TABLE.entrySet()) {
+            if (idEntry.getValue().equals(id)) {
+                return idEntry.getKey();
+            }
+        }
+        return null;
+    }
+
+    public static Long convertIdToSensLogId(String fcUnitId) {
+        return getIdForType(fcUnitId);
+    }
+
+    public static String convertIdToAzureId(Long slUnitId) {
+        return getAzureId(slUnitId);
+    }
+}

+ 6 - 1
connector-model/src/main/java/cz/senslog/connector/model/converter/FieldClimateModelSenslogV1ModelConverter.java

@@ -32,6 +32,11 @@ public class FieldClimateModelSenslogV1ModelConverter implements Converter<Field
     @Override
     @Override
     public SenslogV1Model convert(FieldClimateModel model) {
     public SenslogV1Model convert(FieldClimateModel model) {
 
 
+        if (model == null || model.getStations() == null) {
+            logger.warn("Nothing to convert. Received model is empty.");
+            return null;
+        }
+
         List<StationData> stations = model.getStations();
         List<StationData> stations = model.getStations();
 
 
         int size = 0;
         int size = 0;
@@ -47,7 +52,7 @@ public class FieldClimateModelSenslogV1ModelConverter implements Converter<Field
             int locationMapInitSize = station.getData().size() * countOfGroup(POSITION);
             int locationMapInitSize = station.getData().size() * countOfGroup(POSITION);
             Map<ZonedDateTime, Position> locationData = new HashMap<>(locationMapInitSize);
             Map<ZonedDateTime, Position> locationData = new HashMap<>(locationMapInitSize);
 
 
-            Long unitId = Long.parseLong(station.getId(), 16);
+            Long unitId = FieldClimateUnitConverter.convertIdToSensLogId(station.getId());
 
 
             for (SensorDataInfo sensor : station.getSensors()) {
             for (SensorDataInfo sensor : station.getSensors()) {
 
 

+ 20 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/FieldClimateUnitConverter.java

@@ -0,0 +1,20 @@
+package cz.senslog.connector.model.converter;
+
+public class FieldClimateUnitConverter {
+
+    public static Long convertIdToSensLogId(String fcUnitId) {
+        if (fcUnitId == null) return null;
+        return Long.parseLong(fcUnitId, 16);
+    }
+
+    public static String convertIdToFieldClimateId(Long slUnitId) {
+        if (slUnitId == null) return null;
+        String hexVal = Long.toHexString(slUnitId);
+        StringBuilder res = new StringBuilder();
+        for (int i = hexVal.length(); i < 8; i++) {
+            res.append("0");
+        }
+        res.append(hexVal);
+        return res.toString();
+    }
+}

+ 8 - 6
connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1Pusher.java

@@ -83,9 +83,11 @@ class  SenslogV1Pusher implements ConnectorPusher<SenslogV1Model> {
             observationQueue.addAll(observations);
             observationQueue.addAll(observations);
         }
         }
 
 
-        logger.info("Adding {} failed observations to the queue.", failedObservations.size());
-        observationQueue.addAll(failedObservations);
-        failedObservations.clear();
+        if (!failedObservations.isEmpty()) {
+            logger.info("Adding {} failed observations to the queue.", failedObservations.size());
+            observationQueue.addAll(failedObservations);
+            failedObservations.clear();
+        }
 
 
         int counter = 0;
         int counter = 0;
         while (!observationQueue.isEmpty()) {
         while (!observationQueue.isEmpty()) {
@@ -134,7 +136,7 @@ class  SenslogV1Pusher implements ConnectorPusher<SenslogV1Model> {
             }
             }
 
 
             logger.debug("Parsing body of the response.");
             logger.debug("Parsing body of the response.");
-            Boolean result = Boolean.valueOf(response.getBody());
+            boolean result = Boolean.parseBoolean(response.getBody());
 
 
             if (!result) {
             if (!result) {
                 logger.warn("Observation {} was rejected.", id);
                 logger.warn("Observation {} was rejected.", id);
@@ -159,8 +161,8 @@ class  SenslogV1Pusher implements ConnectorPusher<SenslogV1Model> {
 
 
         if (!validateRequiredValues(
         if (!validateRequiredValues(
                 position::getLatitude,
                 position::getLatitude,
-                position::getLongitude)
-        ) {
+                position::getLongitude
+        )) {
             return null;
             return null;
         }
         }
 
 

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

@@ -38,7 +38,7 @@ class SenslogV1ConfigTest {
 
 
         PropertyNotFoundException exception = assertThrows(PropertyNotFoundException.class, () -> new SenslogV1Config(defaultConfig));
         PropertyNotFoundException exception = assertThrows(PropertyNotFoundException.class, () -> new SenslogV1Config(defaultConfig));
 
 
-        assertEquals("Property 'senslog/host' does not exist.", exception.getMessage());
+        assertEquals("Property 'senslog.host' does not exist.", exception.getMessage());
     }
     }
 
 
     @Test
     @Test

+ 0 - 5
docker-compose.yaml

@@ -27,8 +27,3 @@ services:
         APP_PARAMS: -cf config/fieldclimateSenslog1.yaml
         APP_PARAMS: -cf config/fieldclimateSenslog1.yaml
         DEBUG: "true"
         DEBUG: "true"
         LOG_MONITOR: "false"
         LOG_MONITOR: "false"
-
-networks:
-  default:
-    external:
-      name: elasticsearch-elk_default