Added ServiceController and Service supervision 82/1982/5
authorPatrikBuhr <patrik.buhr@est.tech>
Mon, 9 Dec 2019 15:39:11 +0000 (16:39 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Tue, 10 Dec 2019 08:53:31 +0000 (09:53 +0100)
Checking application alive each minute.

Change-Id: Ia562b349355f7c2bdded0595f348f0c96c9ccbda
Issue-ID: NONRTRIC-84
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
21 files changed:
policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java [moved from policy-agent/src/main/java/org/oransc/policyagent/Beans.java with 91% similarity]
policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfig.java
policy-agent/src/main/java/org/oransc/policyagent/configuration/EnvironmentProcessor.java
policy-agent/src/main/java/org/oransc/policyagent/configuration/RicConfig.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/StartupService.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Policies.java
policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyTypes.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java
policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java
policy-agent/src/test/java/org/oransc/policyagent/controllers/StartupServiceTest.java

@@ -24,11 +24,12 @@ import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Rics;
+import org.oransc.policyagent.repository.Services;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-public class Beans {
+class BeanFactory {
     @Bean
     public Policies getPolicies() {
         return new Policies();
@@ -49,4 +50,9 @@ public class Beans {
         return new ApplicationConfig();
     }
 
+    @Bean
+    Services getServices() {
+        return new Services();
+    }
+
 }
index 04471c8..c5cd44a 100644 (file)
@@ -54,6 +54,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
 import reactor.core.Disposable;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -108,9 +109,9 @@ public class ApplicationConfig {
         loadConfigurationFromFile(this.filepath);
 
         refreshConfigTask = createRefreshTask() //
-                .subscribe(e -> logger.info("Refreshed configuration data"),
-                        throwable -> logger.error("Configuration refresh terminated due to exception", throwable),
-                        () -> logger.error("Configuration refresh terminated"));
+            .subscribe(e -> logger.info("Refreshed configuration data"),
+                throwable -> logger.error("Configuration refresh terminated due to exception", throwable),
+                () -> logger.error("Configuration refresh terminated"));
     }
 
     Mono<EnvProperties> getEnvironment(Properties systemEnvironment) {
@@ -119,10 +120,10 @@ public class ApplicationConfig {
 
     Flux<ApplicationConfig> createRefreshTask() {
         return getEnvironment(systemEnvironment) //
-                .flatMap(this::createCbsClient) //
-                .flatMapMany(this::periodicConfigurationUpdates) //
-                .map(this::parseRicConfigurationfromConsul) //
-                .onErrorResume(this::onErrorResume);
+            .flatMap(this::createCbsClient) //
+            .flatMapMany(this::periodicConfigurationUpdates) //
+            .map(this::parseRicConfigurationfromConsul) //
+            .onErrorResume(this::onErrorResume);
     }
 
     Mono<CbsClient> createCbsClient(EnvProperties env) {
@@ -153,7 +154,7 @@ public class ApplicationConfig {
         return this;
     }
 
-    private synchronized void setConfiguration(@NotNull  Vector<RicConfig> ricConfigs) {
+    private synchronized void setConfiguration(@NotNull Vector<RicConfig> ricConfigs) {
         this.ricConfigs = ricConfigs;
     }
 
index c2033f4..5987c94 100644 (file)
  */
 
 package org.oransc.policyagent.configuration;
+
 import java.util.Optional;
 import java.util.Properties;
+
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties;
 import org.oransc.policyagent.exceptions.EnvironmentLoaderException;
@@ -41,11 +43,11 @@ class EnvironmentProcessor {
         EnvProperties envProperties;
         try {
             envProperties = ImmutableEnvProperties.builder() //
-                    .consulHost(getConsulHost(systemEnvironment)) //
-                    .consulPort(getConsultPort(systemEnvironment)) //
-                    .cbsName(getConfigBindingService(systemEnvironment)) //
-                    .appName(getService(systemEnvironment)) //
-                    .build();
+                .consulHost(getConsulHost(systemEnvironment)) //
+                .consulPort(getConsultPort(systemEnvironment)) //
+                .cbsName(getConfigBindingService(systemEnvironment)) //
+                .appName(getService(systemEnvironment)) //
+                .build();
         } catch (EnvironmentLoaderException e) {
             return Mono.error(e);
         }
@@ -55,27 +57,27 @@ class EnvironmentProcessor {
 
     private static String getConsulHost(Properties systemEnvironments) throws EnvironmentLoaderException {
         return Optional.ofNullable(systemEnvironments.getProperty("CONSUL_HOST"))
-                .orElseThrow(() -> new EnvironmentLoaderException("$CONSUL_HOST environment has not been defined"));
+            .orElseThrow(() -> new EnvironmentLoaderException("$CONSUL_HOST environment has not been defined"));
     }
 
     private static Integer getConsultPort(Properties systemEnvironments) {
         return Optional.ofNullable(systemEnvironments.getProperty("CONSUL_PORT")) //
-                .map(Integer::valueOf) //
-                .orElseGet(EnvironmentProcessor::getDefaultPortOfConsul);
+            .map(Integer::valueOf) //
+            .orElseGet(EnvironmentProcessor::getDefaultPortOfConsul);
     }
 
     private static String getConfigBindingService(Properties systemEnvironments) throws EnvironmentLoaderException {
         return Optional.ofNullable(systemEnvironments.getProperty("CONFIG_BINDING_SERVICE")) //
-                .orElseThrow(
-                        () -> new EnvironmentLoaderException("$CONFIG_BINDING_SERVICE environment has not been defined"));
+            .orElseThrow(
+                () -> new EnvironmentLoaderException("$CONFIG_BINDING_SERVICE environment has not been defined"));
     }
 
     private static String getService(Properties systemEnvironments) throws EnvironmentLoaderException {
         return Optional
-                .ofNullable(Optional.ofNullable(systemEnvironments.getProperty("HOSTNAME"))
-                        .orElse(systemEnvironments.getProperty("SERVICE_NAME")))
-                .orElseThrow(() -> new EnvironmentLoaderException(
-                        "Neither $HOSTNAME/$SERVICE_NAME have not been defined as system environment"));
+            .ofNullable(Optional.ofNullable(systemEnvironments.getProperty("HOSTNAME"))
+                .orElse(systemEnvironments.getProperty("SERVICE_NAME")))
+            .orElseThrow(() -> new EnvironmentLoaderException(
+                "Neither $HOSTNAME/$SERVICE_NAME have not been defined as system environment"));
     }
 
     private static Integer getDefaultPortOfConsul() {
index b2d0fab..8e34aa9 100644 (file)
@@ -21,9 +21,10 @@ package org.oransc.policyagent.controllers;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+
 import java.util.Collection;
 import java.util.Vector;
-import org.oransc.policyagent.Beans;
+
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.oransc.policyagent.repository.ImmutablePolicy;
@@ -32,6 +33,7 @@ import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Rics;
+import org.oransc.policyagent.repository.Services;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -48,25 +50,26 @@ public class PolicyController {
     private final Rics rics;
     private final PolicyTypes types;
     private final Policies policies;
+    private final Services services;
     private static Gson gson = new GsonBuilder() //
         .serializeNulls() //
         .create(); //
 
     @Autowired
-    PolicyController(Beans beans) {
-        this.appConfig = beans.getApplicationConfig();
-        this.rics = beans.getRics();
-        this.types = beans.getPolicyTypes();
-        this.policies = beans.getPolicies();
+    PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics, Services services) {
+        this.appConfig = config;
+        this.types = types;
+        this.policies = policies;
+        this.rics = rics;
+        this.services = services;
     }
 
     @GetMapping("/policy")
     public ResponseEntity<String> getPolicy( //
-        @RequestParam(name = "instance", required = false, defaultValue = "new") String instance) {
+        @RequestParam(name = "instance", required = true) String instance) {
         try {
             Policy p = policies.get(instance);
             return new ResponseEntity<String>(p.json(), HttpStatus.OK);
-
         } catch (ServiceException e) {
             return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
         }
@@ -136,6 +139,7 @@ public class PolicyController {
         @RequestBody String jsonBody) {
 
         try {
+            services.getService(service).ping();
             Ric ricObj = rics.getRic(ric);
             Policy policy = ImmutablePolicy.builder() //
                 .id(instanceId) //
index 040ca3a..f126894 100644 (file)
@@ -26,7 +26,7 @@ import org.immutables.value.Value;
 
 @Value.Immutable
 @Gson.TypeAdapters
-public interface PolicyInfo {
+interface PolicyInfo {
 
     @SerializedName("id")
     public String name();
index 3d59e68..04e2cc5 100644 (file)
@@ -28,7 +28,7 @@ import org.immutables.value.Value;
 
 @Value.Immutable
 @Gson.TypeAdapters
-public interface RicInfo {
+interface RicInfo {
 
     @SerializedName("name")
     public String name();
index 7c63dcd..5980fa4 100644 (file)
@@ -22,11 +22,14 @@ package org.oransc.policyagent.controllers;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+
 import java.util.Optional;
 import java.util.Vector;
+
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.configuration.RicConfig;
 import org.oransc.policyagent.repository.Ric;
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java
new file mode 100644 (file)
index 0000000..86335b6
--- /dev/null
@@ -0,0 +1,116 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.policyagent.controllers;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.Vector;
+
+import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.exceptions.ServiceException;
+import org.oransc.policyagent.repository.Service;
+import org.oransc.policyagent.repository.Services;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class ServiceController {
+
+    private final ApplicationConfig appConfig;
+    private final Services services;
+    private static Gson gson = new GsonBuilder() //
+        .serializeNulls() //
+        .create(); //
+
+    @Autowired
+    ServiceController(ApplicationConfig config, Services services) {
+        this.appConfig = config;
+        this.services = services;
+    }
+
+    @GetMapping("/service")
+    public ResponseEntity<String> getService( //
+        @RequestParam(name = "name", required = true) String name) {
+        try {
+            Service s = services.getService(name);
+            String res = gson.toJson(toServiceStatus(s));
+            return new ResponseEntity<String>(res, HttpStatus.OK);
+
+        } catch (ServiceException e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+        }
+    }
+
+    private ServiceStatus toServiceStatus(Service s) {
+        return ImmutableServiceStatus.builder() //
+            .name(s.getName()) //
+            .keepAliveInterval(s.getKeepAliveInterval().toSeconds()) //
+            .timeSincePing(s.timeSinceLastPing().toSeconds()) //
+            .build();
+    }
+
+    @PutMapping("/service")
+    public ResponseEntity<String> putService( //
+        @RequestBody String jsonBody) {
+        try {
+            ServiceRegistrationInfo s = gson.fromJson(jsonBody, ImmutableServiceRegistrationInfo.class);
+            this.services.put(toService(s));
+            return new ResponseEntity<String>("OK", HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+        }
+    }
+
+    private Service toService(ServiceRegistrationInfo s) {
+        return new Service(s.name(), Duration.ofSeconds(s.keepAliveInterval()));
+    }
+
+    @GetMapping("/services")
+    public ResponseEntity<?> getServices() {
+        Collection<Service> allServices = this.services.getAll();
+        Collection<ServiceStatus> result = new Vector<>(allServices.size());
+        for (Service s : allServices) {
+            result.add(toServiceStatus(s));
+        }
+        return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+    }
+
+    @PutMapping("/service/ping")
+    public ResponseEntity<String> ping( //
+        @RequestBody String name) {
+        try {
+            Service s = services.getService(name);
+            s.ping();
+            return new ResponseEntity<String>("OK", HttpStatus.OK);
+        } catch (ServiceException e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+        }
+    }
+
+}
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java
new file mode 100644 (file)
index 0000000..ede35de
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.policyagent.controllers;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+@Value.Immutable
+@Gson.TypeAdapters
+public interface ServiceRegistrationInfo {
+
+    @SerializedName("name")
+    public String name();
+
+    @SerializedName("keepAlive")
+    public long keepAliveInterval();
+
+}
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java
new file mode 100644 (file)
index 0000000..64647b8
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.policyagent.controllers;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+@Value.Immutable
+@Gson.TypeAdapters
+public interface ServiceStatus {
+
+    @SerializedName("name")
+    public String name();
+
+    @SerializedName("keepAlive")
+    public long keepAliveInterval();
+
+    @SerializedName("timeSincePing")
+    public long timeSincePing();
+
+}
index 12866ac..40f01ca 100644 (file)
@@ -21,6 +21,7 @@
 package org.oransc.policyagent.controllers;
 
 import java.util.Vector;
+
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.configuration.RicConfig;
 import org.oransc.policyagent.repository.Ric;
index 5f88788..cddd8a3 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
+
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
index d3c2c4f..6cf8ff4 100644 (file)
@@ -22,6 +22,7 @@ package org.oransc.policyagent.repository;
 
 import java.util.HashMap;
 import java.util.Map;
+
 import org.oransc.policyagent.exceptions.ServiceException;
 
 public class PolicyTypes {
index ffe493d..066c005 100644 (file)
@@ -21,6 +21,7 @@
 package org.oransc.policyagent.repository;
 
 import java.util.Vector;
+
 import org.oransc.policyagent.configuration.RicConfig;
 
 /**
index 8efa542..6a26b98 100644 (file)
  */
 package org.oransc.policyagent.repository;
 
-public interface Service {
+import java.time.Duration;
+import java.time.Instant;
+
+public class Service {
+    private final String name;
+    private final Duration keepAliveInterval;
+    private Instant lastPing;
+    // private final String callbackUrl1; // TBD
+
+    public Service(String name, Duration keepAliveInterval) {
+        this.name = name;
+        this.keepAliveInterval = keepAliveInterval;
+        ping();
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public Duration getKeepAliveInterval() {
+        return this.keepAliveInterval;
+    }
+
+    public synchronized void ping() {
+        this.lastPing = Instant.now();
+    }
+
+    public synchronized boolean isExpired() {
+        return timeSinceLastPing().compareTo(this.keepAliveInterval) > 0;
+    }
+
+    public synchronized Duration timeSinceLastPing() {
+        return Duration.between(this.lastPing, Instant.now());
+    }
 
 }
index 5b5b4a8..789ea2e 100644 (file)
 
 package org.oransc.policyagent.repository;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.oransc.policyagent.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,4 +36,24 @@ public class Services {
     public Services() {
     }
 
+    public synchronized Service getService(String name) throws ServiceException {
+        Service s = services.get(name);
+        if (s == null) {
+            throw new ServiceException("Could not find service: " + name);
+        }
+        return s;
+    }
+
+    public synchronized Service get(String name) {
+        return services.get(name);
+    }
+
+    public synchronized void put(Service service) {
+        services.put(service.getName(), service);
+    }
+
+    public synchronized Collection<Service> getAll() {
+        return services.values();
+    }
+
 }
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java
new file mode 100644 (file)
index 0000000..03479dd
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.policyagent.tasks;
+
+import org.oransc.policyagent.repository.Policies;
+import org.oransc.policyagent.repository.Policy;
+import org.oransc.policyagent.repository.Service;
+import org.oransc.policyagent.repository.Services;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Flux;
+
+@Component
+@EnableScheduling
+public class ServiceSupervision {
+    private static final Logger logger = LoggerFactory.getLogger(ServiceSupervision.class);
+    private final Services services;
+    private final Policies policies;
+
+    @Autowired
+    public ServiceSupervision(Services services, Policies policies) {
+        this.services = services;
+        this.policies = policies;
+    }
+
+    @Scheduled(fixedRate = 1000 * 60)
+    public void checkAllServices() {
+        logger.debug("Checking services starting");
+        createTask().subscribe(this::onPolicyDeleted, this::onError, this::onComplete);
+    }
+
+    private void onPolicyDeleted(Policy policy) {
+        logger.info("Policy deleted due to inactivity: " + policy.id() + ", service: " + policy.ownerServiceName());
+    }
+
+    private void onError(Throwable t) {
+        logger.error("Service supervision failed", t);
+    }
+
+    private void onComplete() {
+        logger.debug("Checking services completed");
+    }
+
+    Flux<Policy> createTask() {
+        return Flux.fromIterable(services.getAll()) //
+            .filter(service -> service.isExpired()) //
+            .doOnNext(service -> logger.info("Service is expired:" + service.getName()))
+            .flatMap(service -> getAllPolicies(service)) //
+            .flatMap(policy -> deletePolicy(policy));
+    }
+
+    Flux<Policy> getAllPolicies(Service service) {
+        return Flux.fromIterable(policies.getForService(service.getName()));
+    }
+
+    Flux<Policy> deletePolicy(Policy policy) {
+        this.policies.remove(policy);
+        return Flux.just(policy);
+    }
+
+}
index 18ea22b..3b98ef5 100644 (file)
@@ -21,10 +21,19 @@ package org.oransc.policyagent;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertFalse;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
 import java.net.URL;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.controllers.ImmutableServiceRegistrationInfo;
+import org.oransc.policyagent.controllers.ImmutableServiceStatus;
+import org.oransc.policyagent.controllers.ServiceRegistrationInfo;
+import org.oransc.policyagent.controllers.ServiceStatus;
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.oransc.policyagent.repository.ImmutablePolicy;
 import org.oransc.policyagent.repository.ImmutablePolicyType;
@@ -44,13 +53,22 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.web.client.RestTemplate;
 
-
 @RunWith(SpringRunner.class)
 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 public class ApplicationTest {
 
     @Autowired
-    private Beans beans;
+    private Rics rics;
+
+    @Autowired
+    private Policies policies;
+
+    @Autowired
+    private PolicyTypes policyTypes;
+
+    private static Gson gson = new GsonBuilder() //
+        .serializeNulls() //
+        .create(); //
 
     static class MockApplicationConfig extends ApplicationConfig {
         @Override
@@ -60,28 +78,17 @@ public class ApplicationTest {
         }
     }
 
-
+    /**
+     * overrides the BeanFactory
+     */
     @TestConfiguration
-    static class Beans {
-        @Bean
-        public Rics getRics() {
-            return new Rics();
-        }
-
-        @Bean
-        public Policies getPolicies() {
-            return new Policies();
-        }
-
-        @Bean
-        public PolicyTypes getPolicyTypes() {
-            return new PolicyTypes();
-        }
+    static class BeanFactory {
 
         @Bean
         public ApplicationConfig getApplicationConfig() {
             return new MockApplicationConfig();
         }
+
     }
 
     @LocalServerPort
@@ -91,15 +98,15 @@ public class ApplicationTest {
 
     @Test
     public void getRics() throws Exception {
-        String cmd = "/rics";
-        String rsp = this.restTemplate.getForObject("http://localhost:" + port + cmd, String.class);
+        String url = baseUrl() + "/rics";
+        String rsp = this.restTemplate.getForObject(url, String.class);
         assertThat(rsp).contains("kista_1");
     }
 
     @Test
     public void getRic() throws Exception {
-        String cmd = "/ric?managedElementId=kista_1";
-        String rsp = this.restTemplate.getForObject("http://localhost:" + port + cmd, String.class);
+        String url = baseUrl() + "/ric?managedElementId=kista_1";
+        String rsp = this.restTemplate.getForObject(url, String.class);
         assertThat(rsp).isEqualTo("ric1");
     }
 
@@ -107,13 +114,15 @@ public class ApplicationTest {
 
     @Test
     public void putPolicy() throws Exception {
-        String url = "http://localhost:" + port + "/policy?type=type1&instance=instance1&ric=ric1&service=service1";
+        putService("service1");
+
+        String url = baseUrl() + "/policy?type=type1&instance=instance1&ric=ric1&service=service1";
         String json = "{}";
         addPolicyType("type1");
 
         this.restTemplate.put(url, json);
 
-        Policy policy = beans.getPolicies().get("instance1");
+        Policy policy = policies.get("instance1");
 
         assertThat(policy).isNotNull();
         assertThat(policy.id()).isEqualTo("instance1");
@@ -122,35 +131,39 @@ public class ApplicationTest {
 
     private PolicyType addPolicyType(String name) {
         PolicyType type = ImmutablePolicyType.builder() //
-                .jsonSchema("") //
-                .name(name) //
-                .build();
+            .jsonSchema("") //
+            .name(name) //
+            .build();
 
-        beans.getPolicyTypes().put(type);
+        policyTypes.put(type);
         return type;
     }
 
     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
         Policy p = ImmutablePolicy.builder().id(id) //
-                .json("{}") //
-                .ownerServiceName(service) //
-                .ric(beans.getRics().getRic("ric1")) //
-                .type(addPolicyType(typeName)) //
-                .build();
-        beans.getPolicies().put(p);
+            .json("{}") //
+            .ownerServiceName(service) //
+            .ric(rics.getRic("ric1")) //
+            .type(addPolicyType(typeName)) //
+            .build();
+        policies.put(p);
         return p;
     }
 
+    private String baseUrl() {
+        return "http://localhost:" + port;
+    }
+
     @Test
     public void getPolicy() throws Exception {
-        String url = "http://localhost:" + port + "/policy?instance=id";
+        String url = baseUrl() + "/policy?instance=id";
         Policy policy = addPolicy("id", "typeName", "service1");
         {
             String rsp = this.restTemplate.getForObject(url, String.class);
             assertThat(rsp).isEqualTo(policy.json());
         }
         {
-            beans.getPolicies().remove(policy);
+            policies.remove(policy);
             ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
             assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value());
         }
@@ -158,7 +171,7 @@ public class ApplicationTest {
 
     @Test
     public void getPolicies() throws Exception {
-        String url = "http://localhost:" + port + "/policies";
+        String url = baseUrl() + "/policies";
         addPolicy("id1", "type1", "service1");
         addPolicy("id2", "type2", "service2");
 
@@ -174,20 +187,49 @@ public class ApplicationTest {
         addPolicy("id2", "type1", "service2");
         addPolicy("id3", "type2", "service1");
 
-        String url = "http://localhost:" + port + "/policies?type=type1";
+        String url = baseUrl() + "/policies?type=type1";
         String rsp = this.restTemplate.getForObject(url, String.class);
         System.out.println(rsp);
         assertThat(rsp).contains("id1");
         assertThat(rsp).contains("id2");
         assertFalse(rsp.contains("id3"));
 
-        url = "http://localhost:" + port + "/policies?type=type1&service=service2";
+        url = baseUrl() + "/policies?type=type1&service=service2";
         rsp = this.restTemplate.getForObject(url, String.class);
         System.out.println(rsp);
         assertFalse(rsp.contains("id1"));
         assertThat(rsp).contains("id2");
         assertFalse(rsp.contains("id3"));
+    }
+
+    private void putService(String name) {
+        String url = baseUrl() + "/service";
+
+        ServiceRegistrationInfo service = ImmutableServiceRegistrationInfo.builder() //
+            .keepAliveInterval(1) //
+            .name(name) //
+            .build();
+        String json = gson.toJson(service);
+        this.restTemplate.put(url, json);
+    }
+
+    @Test
+    public void putAndGetService() throws Exception {
+        putService("name");
+
+        String url = baseUrl() + "/service?name=name";
+        String rsp = this.restTemplate.getForObject(url, String.class);
+        ServiceStatus status = gson.fromJson(rsp, ImmutableServiceStatus.class);
+        assertThat(status.keepAliveInterval() == 1);
+        assertThat(status.name().equals("name"));
+
+        url = baseUrl() + "/services";
+        rsp = this.restTemplate.getForObject(url, String.class);
+        assertThat(rsp.contains("name"));
+        System.out.println(rsp);
 
+        url = baseUrl() + "/service/ping";
+        this.restTemplate.put(url, "name");
     }
 
 }
index 0cbb395..c8bbdfd 100644 (file)
@@ -21,30 +21,35 @@ package org.oransc.policyagent.configuration;
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.doReturn;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.Properties;
+
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.read.ListAppender;
+
 import com.google.common.base.Charsets;
 import com.google.common.io.Resources;
 import com.google.gson.JsonIOException;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.JsonSyntaxException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Properties;
+
 import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties;
 import org.oransc.policyagent.utils.LoggingUtils;
+
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 import reactor.test.StepVerifier;
@@ -54,14 +59,13 @@ public class ApplicationConfigTest {
     private ApplicationConfig appConfigUnderTest;
     CbsClient cbsClient = mock(CbsClient.class);
 
-
     private static EnvProperties properties() {
         return ImmutableEnvProperties.builder() //
-                .consulHost("host") //
-                .consulPort(123) //
-                .cbsName("cbsName") //
-                .appName("appName") //
-                .build();
+            .consulHost("host") //
+            .consulPort(123) //
+            .cbsName("cbsName") //
+            .appName("appName") //
+            .build();
     }
 
     @Test
@@ -73,9 +77,7 @@ public class ApplicationConfigTest {
         final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ApplicationConfig.class);
         Flux<ApplicationConfig> task = appConfigUnderTest.createRefreshTask();
 
-        StepVerifier.create(task)
-                .expectSubscription()
-                .verifyComplete();
+        StepVerifier.create(task).expectSubscription().verifyComplete();
 
         assertTrue(logAppender.list.toString().contains("$CONSUL_HOST environment has not been defined"));
     }
@@ -96,12 +98,12 @@ public class ApplicationConfigTest {
         Flux<ApplicationConfig> task = appConfigUnderTest.createRefreshTask();
 
         StepVerifier //
-                .create(task) //
-                .expectSubscription() //
-                .verifyComplete();
+            .create(task) //
+            .expectSubscription() //
+            .verifyComplete();
 
         assertTrue(
-                logAppender.list.toString().contains("Could not refresh application configuration java.io.IOException"));
+            logAppender.list.toString().contains("Could not refresh application configuration java.io.IOException"));
     }
 
     @Test
@@ -119,10 +121,10 @@ public class ApplicationConfigTest {
         Flux<ApplicationConfig> task = appConfigUnderTest.createRefreshTask();
 
         StepVerifier //
-                .create(task) //
-                .expectSubscription() //
-                .expectNext(appConfigUnderTest) //
-                .verifyComplete();
+            .create(task) //
+            .expectSubscription() //
+            .expectNext(appConfigUnderTest) //
+            .verifyComplete();
 
         Assertions.assertNotNull(appConfigUnderTest.getRicConfigs());
     }
index 5900f14..366524f 100644 (file)
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.util.Vector;
+
 import org.junit.Test;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.configuration.ImmutableRicConfig;