Bugfix, RIC configs would disappear after one minute
[nonrtric.git] / policy-agent / src / main / java / org / oransc / policyagent / controllers / ServiceController.java
index cc1d711..bf78742 100644 (file)
@@ -23,13 +23,14 @@ package org.oransc.policyagent.controllers;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Vector;
 
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.oransc.policyagent.repository.Policies;
@@ -41,19 +42,20 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 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
+@Api(tags = "Service registry and supervision")
 public class ServiceController {
 
     private final Services services;
     private final Policies policies;
 
     private static Gson gson = new GsonBuilder() //
-        .serializeNulls() //
         .create(); //
 
     @Autowired
@@ -63,77 +65,112 @@ public class ServiceController {
     }
 
     @GetMapping("/services")
-    @ApiOperation(value = "Returns service information", response = ServiceStatus.class)
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
-    public ResponseEntity<String> getServices( //
+    @ApiOperation(value = "Returns service information")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List"), //
+            @ApiResponse(code = 404, message = "Service is not found", response = String.class)})
+    public ResponseEntity<String> getServices(//
         @RequestParam(name = "name", required = false) String name) {
 
-        Collection<ServiceStatus> servicesStatus = new Vector<>();
-        synchronized (this.services) {
-            for (Service s : this.services.getAll()) {
-                if (name == null || name.equals(s.name())) {
-                    servicesStatus.add(toServiceStatus(s));
-                }
+        if (name != null && this.services.get(name) == null) {
+            return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND);
+        }
+
+        Collection<ServiceStatus> servicesStatus = new ArrayList<>();
+        for (Service s : this.services.getAll()) {
+            if (name == null || name.equals(s.getName())) {
+                servicesStatus.add(toServiceStatus(s));
             }
         }
 
         String res = gson.toJson(servicesStatus);
-        return new ResponseEntity<String>(res, HttpStatus.OK);
+        return new ResponseEntity<>(res, HttpStatus.OK);
     }
 
     private ServiceStatus toServiceStatus(Service s) {
-        return ImmutableServiceStatus.builder() //
-            .name(s.name()) //
-            .keepAliveInterval(s.getKeepAliveInterval().toSeconds()) //
-            .timeSincePing(s.timeSinceLastPing().toSeconds()) //
-            .build();
+        return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(),
+            s.getCallbackUrl());
+    }
+
+    private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo) throws ServiceException {
+        if (registrationInfo.serviceName.isEmpty()) {
+            throw new ServiceException("Missing mandatory parameter 'serviceName'");
+        }
+        if (registrationInfo.keepAliveIntervalSeconds < 0) {
+            throw new ServiceException("Keepalive interval shoul be greater or equal to 0");
+        }
     }
 
+    @ApiOperation(value = "Register a service")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "Service updated", response = String.class),
+            @ApiResponse(code = 201, message = "Service created", response = String.class), //
+            @ApiResponse(code = 400, message = "The ServiceRegistrationInfo is not accepted", response = String.class)})
     @PutMapping("/service")
-    public ResponseEntity<String> putService( //
-        @RequestBody String jsonBody) {
+    public ResponseEntity<String> putService(//
+        @RequestBody ServiceRegistrationInfo registrationInfo) {
         try {
-            ServiceRegistrationInfo s = gson.fromJson(jsonBody, ImmutableServiceRegistrationInfo.class);
-            this.services.put(toService(s));
-            return new ResponseEntity<String>("OK", HttpStatus.OK);
+            validateRegistrationInfo(registrationInfo);
+            final boolean isCreate = this.services.get(registrationInfo.serviceName) == null;
+            this.services.put(toService(registrationInfo));
+            return new ResponseEntity<>("OK", isCreate ? HttpStatus.CREATED : HttpStatus.OK);
         } catch (Exception e) {
-            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
         }
     }
 
+    @ApiOperation(value = "Delete a service")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 204, message = "OK"),
+            @ApiResponse(code = 404, message = "Service not found", response = String.class)})
     @DeleteMapping("/services")
-    public ResponseEntity<String> deleteService( //
-        @RequestParam(name = "name", required = true) String name) {
+    public ResponseEntity<String> deleteService(//
+        @RequestParam(name = "name", required = true) String serviceName) {
         try {
-            Service service = removeService(name);
+            Service service = removeService(serviceName);
             // Remove the policies from the repo and let the consistency monitoring
             // do the rest.
             removePolicies(service);
-            return new ResponseEntity<String>("OK", HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT);
         } catch (Exception e) {
-            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
         }
     }
 
-    private Service removeService(String name) throws ServiceException {
-        synchronized (this.services) {
-            Service service = this.services.getService(name);
-            this.services.remove(service.name());
-            return service;
+    @ApiOperation(value = "Heartbeat from a serice")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "Service supervision timer refreshed, OK"),
+            @ApiResponse(code = 404, message = "The service is not found, needs re-registration")})
+    @PostMapping("/services/keepalive")
+    public ResponseEntity<String> keepAliveService(//
+        @RequestParam(name = "name", required = true) String serviceName) {
+        try {
+            services.getService(serviceName).keepAlive();
+            return new ResponseEntity<>("OK", HttpStatus.OK);
+        } catch (ServiceException e) {
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
         }
     }
 
+    private Service removeService(String name) throws ServiceException {
+        Service service = this.services.getService(name); // Just to verify that it exists
+        this.services.remove(service.getName());
+        return service;
+    }
+
     private void removePolicies(Service service) {
-        synchronized (this.policies) {
-            Vector<Policy> policyList = new Vector<>(this.policies.getForService(service.name()));
-            for (Policy policy : policyList) {
-                this.policies.remove(policy);
-            }
+        Collection<Policy> policyList = this.policies.getForService(service.getName());
+        for (Policy policy : policyList) {
+            this.policies.remove(policy);
         }
     }
 
     private Service toService(ServiceRegistrationInfo s) {
-        return new Service(s.name(), Duration.ofSeconds(s.keepAliveInterval()), s.callbackUrl());
+        return new Service(s.serviceName, Duration.ofSeconds(s.keepAliveIntervalSeconds), s.callbackUrl);
     }
 
 }