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.net.MalformedURLException;
+import java.net.URL;
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;
+import org.oransc.policyagent.repository.Policy;
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.DeleteMapping;
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.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
- ServiceController(Services services) {
+ ServiceController(Services services, Policies policies) {
this.services = services;
+ this.policies = policies;
}
- @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);
+ @GetMapping("/services")
+ @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) {
- } catch (ServiceException e) {
- return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+ 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<>(res, HttpStatus.OK);
}
private ServiceStatus toServiceStatus(Service s) {
- return ImmutableServiceStatus.builder() //
- .name(s.getName()) //
- .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, MalformedURLException {
+ 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");
+ }
+ if (!registrationInfo.callbackUrl.isEmpty()) {
+ new URL(registrationInfo.callbackUrl);
+ }
+ }
+
+ @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);
}
}
- private Service toService(ServiceRegistrationInfo s) {
- return new Service(s.name(), Duration.ofSeconds(s.keepAliveInterval()), s.callbackUrl());
- }
-
- @GetMapping("/services")
- public ResponseEntity<?> getServices() {
- Collection<ServiceStatus> result = new Vector<>();
- synchronized (this.services) {
- for (Service s : this.services.getAll()) {
- result.add(toServiceStatus(s));
- }
+ @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 serviceName) {
+ try {
+ Service service = removeService(serviceName);
+ // Remove the policies from the repo and let the consistency monitoring
+ // do the rest.
+ removePolicies(service);
+ return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT);
+ } catch (Exception e) {
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
- return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
}
- @PutMapping("/service/ping")
- public ResponseEntity<String> ping( //
- @RequestBody String name) {
+ @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")})
+ @PutMapping("/services/keepalive")
+ public ResponseEntity<String> keepAliveService(//
+ @RequestParam(name = "name", required = true) String serviceName) {
try {
- Service s = services.getService(name);
- s.ping();
- return new ResponseEntity<String>("OK", HttpStatus.OK);
+ services.getService(serviceName).keepAlive();
+ return new ResponseEntity<>("OK", HttpStatus.OK);
} catch (ServiceException 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 {
+ Service service = this.services.getService(name); // Just to verify that it exists
+ this.services.remove(service.getName());
+ return service;
+ }
+
+ private void removePolicies(Service service) {
+ 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.serviceName, Duration.ofSeconds(s.keepAliveIntervalSeconds), s.callbackUrl);
+ }
+
}