Merge "Upgraded test env with Kubernetes support"
authorHenrik Andersson <henrik.b.andersson@est.tech>
Fri, 15 Jan 2021 08:29:51 +0000 (08:29 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Fri, 15 Jan 2021 08:29:51 +0000 (08:29 +0000)
21 files changed:
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/ApplicationConfig.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerCallbacks.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerCallbacks.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerController.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJobs.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducers.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiType.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiTypes.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/MultiMap.java
enrichment-coordinator-service/src/main/java/org/oransc/enrichment/tasks/ProducerSupervision.java
enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java
r-app-catalogue/eclipse-formatter.xml [new file with mode: 0644]
r-app-catalogue/pom.xml
r-app-catalogue/src/main/java/org/oransc/rappcatalogue/api/GeneralRappCatalogueControllerAdvisor.java
r-app-catalogue/src/main/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImpl.java
r-app-catalogue/src/main/java/org/oransc/rappcatalogue/exception/HeaderException.java
r-app-catalogue/src/main/java/org/oransc/rappcatalogue/exception/InvalidServiceException.java
r-app-catalogue/src/main/java/org/oransc/rappcatalogue/exception/ServiceNotFoundException.java
r-app-catalogue/src/test/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImplTest.java

index c5d2bec..785ddfc 100644 (file)
@@ -26,6 +26,7 @@ import java.lang.invoke.MethodHandles;
 
 import org.apache.catalina.connector.Connector;
 import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
 import org.oransc.enrichment.repository.EiJobs;
 import org.oransc.enrichment.repository.EiProducers;
 import org.oransc.enrichment.repository.EiTypes;
@@ -46,6 +47,11 @@ class BeanFactory {
     private final ApplicationConfig applicationConfig = new ApplicationConfig();
     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+    private ProducerCallbacks producerCallbacks;
+    private EiTypes eiTypes;
+    private EiJobs eiJobs;
+    private EiProducers eiProducers;
+
     @Bean
     public ObjectMapper mapper() {
         return new ObjectMapper();
@@ -62,23 +68,36 @@ class BeanFactory {
 
     @Bean
     public EiJobs eiJobs() {
-        EiJobs jobs = new EiJobs(getApplicationConfig());
-        try {
-            jobs.restoreJobsFromDatabase();
-        } catch (Exception e) {
-            logger.error("Could not restore jobs from database: {}", e.getMessage());
+        if (eiJobs == null) {
+            eiJobs = new EiJobs(getApplicationConfig(), producerCallbacks());
+            try {
+                eiJobs.restoreJobsFromDatabase();
+            } catch (Exception e) {
+                logger.error("Could not restore jobs from database: {}", e.getMessage());
+            }
         }
-        return jobs;
+        return eiJobs;
     }
 
     @Bean
     public EiTypes eiTypes() {
-        return new EiTypes();
+        if (this.eiTypes == null) {
+            eiTypes = new EiTypes(getApplicationConfig());
+            try {
+                eiTypes.restoreTypesFromDatabase();
+            } catch (Exception e) {
+                logger.error("Could not restore EI types from database: {}", e.getMessage());
+            }
+        }
+        return eiTypes;
     }
 
     @Bean
-    public EiProducers eiProducers() {
-        return new EiProducers();
+    public ProducerCallbacks producerCallbacks() {
+        if (this.producerCallbacks == null) {
+            producerCallbacks = new ProducerCallbacks(getApplicationConfig());
+        }
+        return this.producerCallbacks;
     }
 
     @Bean
index fce9e22..db4201b 100644 (file)
@@ -62,21 +62,26 @@ public class ApplicationConfig {
     @Value("${app.webclient.http.proxy-port:0}")
     private int httpProxyPort = 0;
 
+    private WebClientConfig webClientConfig = null;
+
     public WebClientConfig getWebClientConfig() {
-        HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
-            .httpProxyHost(this.httpProxyHost) //
-            .httpProxyPort(this.httpProxyPort) //
-            .build();
-        return ImmutableWebClientConfig.builder() //
-            .keyStoreType(this.sslKeyStoreType) //
-            .keyStorePassword(this.sslKeyStorePassword) //
-            .keyStore(this.sslKeyStore) //
-            .keyPassword(this.sslKeyPassword) //
-            .isTrustStoreUsed(this.sslTrustStoreUsed) //
-            .trustStore(this.sslTrustStore) //
-            .trustStorePassword(this.sslTrustStorePassword) //
-            .httpProxyConfig(httpProxyConfig) //
-            .build();
+        if (this.webClientConfig == null) {
+            HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
+                .httpProxyHost(this.httpProxyHost) //
+                .httpProxyPort(this.httpProxyPort) //
+                .build();
+            this.webClientConfig = ImmutableWebClientConfig.builder() //
+                .keyStoreType(this.sslKeyStoreType) //
+                .keyStorePassword(this.sslKeyStorePassword) //
+                .keyStore(this.sslKeyStore) //
+                .keyPassword(this.sslKeyPassword) //
+                .isTrustStoreUsed(this.sslTrustStoreUsed) //
+                .trustStore(this.sslTrustStore) //
+                .trustStorePassword(this.sslTrustStorePassword) //
+                .httpProxyConfig(httpProxyConfig) //
+                .build();
+        }
+        return this.webClientConfig;
     }
 
 }
index c222cfa..eb85d37 100644 (file)
@@ -31,8 +31,8 @@ import org.oransc.enrichment.configuration.ApplicationConfig;
 import org.oransc.enrichment.repository.EiJob;
 import org.oransc.enrichment.repository.EiJobs;
 import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiProducers;
 import org.oransc.enrichment.repository.EiType;
-import org.oransc.enrichment.repository.EiTypes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,21 +49,21 @@ public class ConsumerCallbacks {
     private static Gson gson = new GsonBuilder().create();
 
     private final AsyncRestClient restClient;
-    private final EiTypes eiTypes;
     private final EiJobs eiJobs;
+    private final EiProducers eiProducers;
 
     @Autowired
-    public ConsumerCallbacks(ApplicationConfig config, EiTypes eiTypes, EiJobs eiJobs) {
+    public ConsumerCallbacks(ApplicationConfig config, EiJobs eiJobs, EiProducers eiProducers) {
         AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config.getWebClientConfig());
         this.restClient = restClientFactory.createRestClientUseHttpProxy("");
-        this.eiTypes = eiTypes;
         this.eiJobs = eiJobs;
+        this.eiProducers = eiProducers;
     }
 
     public void notifyConsumersProducerDeleted(EiProducer eiProducer) {
         for (EiType type : eiProducer.getEiTypes()) {
-            if (this.eiTypes.get(type.getId()) == null) {
-                // The type is removed
+            if (this.eiProducers.getProducersForType(type).isEmpty()) {
+                // No producers left for the type
                 for (EiJob job : this.eiJobs.getJobsForType(type)) {
                     if (job.isLastStatusReportedEnabled()) {
                         noifyJobOwner(job, new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.DISABLED));
index f45ff73..6e9438d 100644 (file)
@@ -33,7 +33,6 @@ import io.swagger.annotations.ApiResponses;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Vector;
 
 import org.everit.json.schema.Schema;
 import org.everit.json.schema.loader.SchemaLoader;
@@ -46,6 +45,7 @@ import org.oransc.enrichment.exceptions.ServiceException;
 import org.oransc.enrichment.repository.EiJob;
 import org.oransc.enrichment.repository.EiJobs;
 import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiProducers;
 import org.oransc.enrichment.repository.EiType;
 import org.oransc.enrichment.repository.EiTypes;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +77,9 @@ public class ConsumerController {
     @Autowired
     private EiTypes eiTypes;
 
+    @Autowired
+    private EiProducers eiProducers;
+
     @Autowired
     ProducerCallbacks producerCallbacks;
 
@@ -206,11 +209,7 @@ public class ConsumerController {
     }
 
     private Collection<EiProducer> getProducers(EiJob eiJob) {
-        try {
-            return this.eiTypes.getType(eiJob.getTypeId()).getProducers();
-        } catch (Exception e) {
-            return new Vector<>();
-        }
+        return this.eiProducers.getProducersForType(eiJob.getTypeId());
     }
 
     private ConsumerEiJobStatus toEiJobStatus(EiJob job) {
@@ -235,8 +234,7 @@ public class ConsumerController {
         @PathVariable("eiJobId") String eiJobId) {
         try {
             EiJob job = this.eiJobs.getJob(eiJobId);
-            this.eiJobs.remove(job);
-            this.producerCallbacks.notifyProducersJobDeleted(job);
+            this.eiJobs.remove(job, this.eiProducers);
             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
         } catch (Exception e) {
             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
@@ -263,14 +261,14 @@ public class ConsumerController {
         final boolean isNewJob = this.eiJobs.get(eiJobId) == null;
 
         return validatePutEiJob(eiJobId, eiJobObject) //
-            .flatMap(this::notifyProducersNewJob) //
+            .flatMap(this::startEiJob) //
             .doOnNext(newEiJob -> this.eiJobs.put(newEiJob)) //
             .flatMap(newEiJob -> Mono.just(new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK)))
             .onErrorResume(throwable -> Mono.just(ErrorResponse.create(throwable, HttpStatus.NOT_FOUND)));
     }
 
-    private Mono<EiJob> notifyProducersNewJob(EiJob newEiJob) {
-        return this.producerCallbacks.notifyProducersJobStarted(newEiJob) //
+    private Mono<EiJob> startEiJob(EiJob newEiJob) {
+        return this.producerCallbacks.startEiJob(newEiJob, eiProducers) //
             .flatMap(noOfAcceptingProducers -> {
                 if (noOfAcceptingProducers.intValue() > 0) {
                     return Mono.just(newEiJob);
index 00d9c14..45b4475 100644 (file)
@@ -26,7 +26,6 @@ import com.google.gson.GsonBuilder;
 import java.lang.invoke.MethodHandles;
 import java.time.Duration;
 import java.util.Collection;
-import java.util.Vector;
 
 import org.oransc.enrichment.clients.AsyncRestClient;
 import org.oransc.enrichment.clients.AsyncRestClientFactory;
@@ -34,11 +33,9 @@ import org.oransc.enrichment.configuration.ApplicationConfig;
 import org.oransc.enrichment.repository.EiJob;
 import org.oransc.enrichment.repository.EiJobs;
 import org.oransc.enrichment.repository.EiProducer;
-import org.oransc.enrichment.repository.EiTypes;
+import org.oransc.enrichment.repository.EiProducers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -47,7 +44,6 @@ import reactor.util.retry.Retry;
 /**
  * Callbacks to the EiProducer
  */
-@Component
 @SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
 public class ProducerCallbacks {
 
@@ -55,17 +51,14 @@ public class ProducerCallbacks {
     private static Gson gson = new GsonBuilder().create();
 
     private final AsyncRestClient restClient;
-    private final EiTypes eiTypes;
 
-    @Autowired
-    public ProducerCallbacks(ApplicationConfig config, EiTypes eiTypes) {
+    public ProducerCallbacks(ApplicationConfig config) {
         AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config.getWebClientConfig());
         this.restClient = restClientFactory.createRestClientNoHttpProxy("");
-        this.eiTypes = eiTypes;
     }
 
-    public void notifyProducersJobDeleted(EiJob eiJob) {
-        for (EiProducer producer : getProducers(eiJob)) {
+    public void stopEiJob(EiJob eiJob, EiProducers eiProducers) {
+        for (EiProducer producer : getProducersForJob(eiJob, eiProducers)) {
             String url = producer.getJobCallbackUrl() + "/" + eiJob.getId();
             restClient.delete(url) //
                 .subscribe(notUsed -> logger.debug("Producer job deleted OK {}", producer.getId()), //
@@ -81,10 +74,10 @@ public class ProducerCallbacks {
      * @param eiJob an EI job
      * @return the number of producers that returned OK
      */
-    public Mono<Integer> notifyProducersJobStarted(EiJob eiJob) {
+    public Mono<Integer> startEiJob(EiJob eiJob, EiProducers eiProducers) {
         Retry retrySpec = Retry.fixedDelay(1, Duration.ofSeconds(1));
-        return Flux.fromIterable(getProducers(eiJob)) //
-            .flatMap(eiProducer -> notifyProducerJobStarted(eiProducer, eiJob, retrySpec)) //
+        return Flux.fromIterable(getProducersForJob(eiJob, eiProducers)) //
+            .flatMap(eiProducer -> postStartEiJob(eiProducer, eiJob, retrySpec)) //
             .collectList() //
             .flatMap(okResponses -> Mono.just(Integer.valueOf(okResponses.size()))); //
     }
@@ -95,13 +88,13 @@ public class ProducerCallbacks {
      * @param producer
      * @param eiJobs
      */
-    public void restartJobs(EiProducer producer, EiJobs eiJobs) {
+    public void restartEiJobs(EiProducer producer, EiJobs eiJobs) {
         final int maxNoOfParalellRequests = 10;
         Retry retrySpec = Retry.backoff(3, Duration.ofSeconds(1));
 
         Flux.fromIterable(producer.getEiTypes()) //
             .flatMap(type -> Flux.fromIterable(eiJobs.getJobsForType(type))) //
-            .flatMap(job -> notifyProducerJobStarted(producer, job, retrySpec), maxNoOfParalellRequests) //
+            .flatMap(job -> postStartEiJob(producer, job, retrySpec), maxNoOfParalellRequests) //
             .onErrorResume(t -> {
                 logger.error("Could not restart EI Job for producer: {}, reason :{}", producer.getId(), t.getMessage());
                 return Flux.empty();
@@ -109,7 +102,7 @@ public class ProducerCallbacks {
             .subscribe();
     }
 
-    private Mono<String> notifyProducerJobStarted(EiProducer producer, EiJob eiJob, Retry retrySpec) {
+    private Mono<String> postStartEiJob(EiProducer producer, EiJob eiJob, Retry retrySpec) {
         ProducerJobInfo request = new ProducerJobInfo(eiJob);
         String body = gson.toJson(request);
 
@@ -122,12 +115,8 @@ public class ProducerCallbacks {
             });
     }
 
-    private Collection<EiProducer> getProducers(EiJob eiJob) {
-        try {
-            return this.eiTypes.getType(eiJob.getTypeId()).getProducers();
-        } catch (Exception e) {
-            return new Vector<>();
-        }
+    private Collection<EiProducer> getProducersForJob(EiJob eiJob, EiProducers eiProducers) {
+        return eiProducers.getProducersForType(eiJob.getTypeId());
     }
 
 }
index e517b3a..e773117 100644 (file)
@@ -35,7 +35,6 @@ import java.util.List;
 
 import org.oransc.enrichment.controllers.ErrorResponse;
 import org.oransc.enrichment.controllers.VoidResponse;
-import org.oransc.enrichment.controllers.consumer.ConsumerCallbacks;
 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
 import org.oransc.enrichment.repository.EiJob;
 import org.oransc.enrichment.repository.EiJobs;
@@ -43,6 +42,8 @@ import org.oransc.enrichment.repository.EiProducer;
 import org.oransc.enrichment.repository.EiProducers;
 import org.oransc.enrichment.repository.EiType;
 import org.oransc.enrichment.repository.EiTypes;
+import org.oransc.enrichment.repository.ImmutableEiProducerRegistrationInfo;
+import org.oransc.enrichment.repository.ImmutableEiTypeRegistrationInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -74,12 +75,6 @@ public class ProducerController {
     @Autowired
     private EiProducers eiProducers;
 
-    @Autowired
-    ProducerCallbacks producerCallbacks;
-
-    @Autowired
-    ConsumerCallbacks consumerCallbacks;
-
     @GetMapping(path = ProducerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation(value = "EI type identifiers", notes = "")
     @ApiResponses(
@@ -233,33 +228,13 @@ public class ProducerController {
         @RequestBody ProducerRegistrationInfo registrationInfo) {
         try {
             EiProducer previousDefinition = this.eiProducers.get(eiProducerId);
-            if (previousDefinition != null) {
-                for (EiType type : previousDefinition.getEiTypes()) {
-                    type.removeProducer(previousDefinition);
-                }
-            }
-
-            EiProducer producer = registerProducer(eiProducerId, registrationInfo);
-            if (previousDefinition != null) {
-                purgeTypes(previousDefinition.getEiTypes());
-                this.consumerCallbacks.notifyConsumersProducerDeleted(previousDefinition);
-            }
-            this.consumerCallbacks.notifyConsumersProducerAdded(producer);
-
+            this.eiProducers.registerProducer(toEiProducerRegistrationInfo(eiProducerId, registrationInfo));
             return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
         } catch (Exception e) {
             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
         }
     }
 
-    private void purgeTypes(Collection<EiType> types) {
-        for (EiType type : types) {
-            if (type.getProducerIds().isEmpty()) {
-                this.eiTypes.remove(type);
-            }
-        }
-    }
-
     @DeleteMapping(
         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
         produces = MediaType.APPLICATION_JSON_VALUE)
@@ -272,45 +247,14 @@ public class ProducerController {
     public ResponseEntity<Object> deleteEiProducer(@PathVariable("eiProducerId") String eiProducerId) {
         try {
             final EiProducer producer = this.eiProducers.getProducer(eiProducerId);
-            this.eiProducers.deregisterProducer(producer, this.eiTypes, this.eiJobs);
-            this.consumerCallbacks.notifyConsumersProducerDeleted(producer);
+            this.eiProducers.deregisterProducer(producer, this.eiTypes);
             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
         } catch (Exception e) {
             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
         }
     }
 
-    private EiType registerType(ProducerEiTypeRegistrationInfo typeInfo) {
-        EiType type = this.eiTypes.get(typeInfo.eiTypeId);
-        if (type == null) {
-            type = new EiType(typeInfo.eiTypeId, typeInfo.jobDataSchema);
-            this.eiTypes.put(type);
-            this.consumerCallbacks.notifyConsumersTypeAdded(type);
-        }
-        return type;
-    }
-
-    EiProducer createProducer(Collection<EiType> types, String producerId, ProducerRegistrationInfo registrationInfo) {
-        return new EiProducer(producerId, types, registrationInfo.jobCallbackUrl,
-            registrationInfo.producerSupervisionCallbackUrl);
-    }
-
-    private EiProducer registerProducer(String producerId, ProducerRegistrationInfo registrationInfo) {
-        ArrayList<EiType> typesForProducer = new ArrayList<>();
-        EiProducer producer = createProducer(typesForProducer, producerId, registrationInfo);
-        for (ProducerEiTypeRegistrationInfo typeInfo : registrationInfo.types) {
-            EiType type = registerType(typeInfo);
-            typesForProducer.add(type);
-            type.addProducer(producer); //
-        }
-        this.eiProducers.put(producer);
-
-        producerCallbacks.restartJobs(producer, this.eiJobs);
-
-        return producer;
-    }
-
-    ProducerRegistrationInfo toEiProducerRegistrationInfo(EiProducer p) {
+    private ProducerRegistrationInfo toEiProducerRegistrationInfo(EiProducer p) {
         Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
         for (EiType type : p.getEiTypes()) {
             types.add(toEiTypeRegistrationInfo(type));
@@ -323,6 +267,26 @@ public class ProducerController {
     }
 
     private ProducerEiTypeInfo toEiTypeInfo(EiType t) {
-        return new ProducerEiTypeInfo(t.getJobDataSchema(), t.getProducerIds());
+        Collection<String> producerIds = this.eiProducers.getProducerIdsForType(t.getId());
+        return new ProducerEiTypeInfo(t.getJobDataSchema(), producerIds);
+    }
+
+    private EiProducers.EiProducerRegistrationInfo toEiProducerRegistrationInfo(String eiProducerId,
+        ProducerRegistrationInfo info) {
+        Collection<EiProducers.EiTypeRegistrationInfo> supportedTypes = new ArrayList<>();
+        for (ProducerEiTypeRegistrationInfo typeInfo : info.types) {
+            EiProducers.EiTypeRegistrationInfo i = ImmutableEiTypeRegistrationInfo.builder() //
+                .id(typeInfo.eiTypeId) //
+                .jobDataSchema(typeInfo.jobDataSchema) //
+                .build();
+            supportedTypes.add(i);
+        }
+        return ImmutableEiProducerRegistrationInfo.builder() //
+            .id(eiProducerId) //
+            .jobCallbackUrl(info.jobCallbackUrl) //
+            .producerSupervisionCallbackUrl(info.producerSupervisionCallbackUrl) //
+            .supportedTypes(supportedTypes) //
+            .build();
     }
+
 }
index bff5be2..f5224f2 100644 (file)
@@ -39,6 +39,7 @@ import java.util.ServiceLoader;
 import java.util.Vector;
 
 import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
 import org.oransc.enrichment.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,11 +58,14 @@ public class EiJobs {
     private final ApplicationConfig config;
     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-    public EiJobs(ApplicationConfig config) {
+    private final ProducerCallbacks producerCallbacks;
+
+    public EiJobs(ApplicationConfig config, ProducerCallbacks producerCallbacks) {
         this.config = config;
         GsonBuilder gsonBuilder = new GsonBuilder();
         ServiceLoader.load(TypeAdapterFactory.class).forEach(gsonBuilder::registerTypeAdapterFactory);
         this.gson = gsonBuilder.create();
+        this.producerCallbacks = producerCallbacks;
     }
 
     public synchronized void restoreJobsFromDatabase() throws IOException {
@@ -71,13 +75,13 @@ public class EiJobs {
         for (File file : dbDir.listFiles()) {
             String json = Files.readString(file.toPath());
             EiJob job = gson.fromJson(json, EiJob.class);
-            this.put(job, false);
+            this.doPut(job);
         }
-
     }
 
     public synchronized void put(EiJob job) {
-        this.put(job, true);
+        this.doPut(job);
+        storeJobInFile(job);
     }
 
     public synchronized Collection<EiJob> getJobs() {
@@ -108,15 +112,15 @@ public class EiJobs {
         return allEiJobs.get(id);
     }
 
-    public synchronized EiJob remove(String id) {
+    public synchronized EiJob remove(String id, EiProducers eiProducers) {
         EiJob job = allEiJobs.get(id);
         if (job != null) {
-            remove(job);
+            remove(job, eiProducers);
         }
         return job;
     }
 
-    public synchronized void remove(EiJob job) {
+    public synchronized void remove(EiJob job, EiProducers eiProducers) {
         this.allEiJobs.remove(job.getId());
         jobsByType.remove(job.getTypeId(), job.getId());
         jobsByOwner.remove(job.getOwner(), job.getId());
@@ -126,7 +130,7 @@ public class EiJobs {
         } catch (IOException e) {
             logger.warn("Could not remove file: {}", e.getMessage());
         }
-
+        this.producerCallbacks.stopEiJob(job, eiProducers);
     }
 
     public synchronized int size() {
@@ -137,6 +141,10 @@ public class EiJobs {
         this.allEiJobs.clear();
         this.jobsByType.clear();
         jobsByOwner.clear();
+        clearDatabase();
+    }
+
+    private void clearDatabase() {
         try {
             FileSystemUtils.deleteRecursively(Path.of(getDatabaseDirectory()));
             Files.createDirectories(Paths.get(getDatabaseDirectory()));
@@ -145,13 +153,10 @@ public class EiJobs {
         }
     }
 
-    private void put(EiJob job, boolean storePersistently) {
+    private void doPut(EiJob job) {
         allEiJobs.put(job.getId(), job);
         jobsByType.put(job.getTypeId(), job.getId(), job);
         jobsByOwner.put(job.getOwner(), job.getId(), job);
-        if (storePersistently) {
-            storeJobInFile(job);
-        }
     }
 
     private void storeJobInFile(EiJob job) {
@@ -173,7 +178,7 @@ public class EiJobs {
     }
 
     private String getDatabaseDirectory() {
-        return config.getVardataDirectory() + "/database";
+        return config.getVardataDirectory() + "/eijobs";
     }
 
 }
index 801e7fc..fcc9156 100644 (file)
 package org.oransc.enrichment.repository;
 
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
 
+import org.immutables.value.Value.Immutable;
+import org.oransc.enrichment.controllers.consumer.ConsumerCallbacks;
+import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
 import org.oransc.enrichment.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 /**
  * Dynamic representation of all EiProducers.
  */
 @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+@Component
 public class EiProducers {
     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
     private final Map<String, EiProducer> allEiProducers = new HashMap<>();
+    private final MultiMap<EiProducer> producersByType = new MultiMap<>();
 
-    public synchronized void put(EiProducer producer) {
+    @Autowired
+    private ProducerCallbacks producerCallbacks;
+
+    @Autowired
+    private ConsumerCallbacks consumerCallbacks;
+
+    @Autowired
+    private EiTypes eiTypes;
+
+    @Autowired
+    private EiJobs eiJobs;
+
+    @Immutable
+    public interface EiTypeRegistrationInfo {
+        String id();
+
+        Object jobDataSchema();
+    }
+
+    @Immutable
+    public interface EiProducerRegistrationInfo {
+        String id();
+
+        Collection<EiTypeRegistrationInfo> supportedTypes();
+
+        String jobCallbackUrl();
+
+        String producerSupervisionCallbackUrl();
+
+    }
+
+    public EiProducer registerProducer(EiProducerRegistrationInfo producerInfo) {
+        final String producerId = producerInfo.id();
+        EiProducer previousDefinition = this.get(producerId);
+        if (previousDefinition != null) {
+            for (EiType type : previousDefinition.getEiTypes()) {
+                producersByType.remove(type.getId(), producerId);
+            }
+            allEiProducers.remove(producerId);
+        }
+
+        EiProducer producer = createProducer(producerInfo);
         allEiProducers.put(producer.getId(), producer);
+        for (EiType type : producer.getEiTypes()) {
+            producersByType.put(type.getId(), producer.getId(), producer);
+        }
+
+        if (previousDefinition != null) {
+            purgeTypes(previousDefinition.getEiTypes());
+            this.consumerCallbacks.notifyConsumersProducerDeleted(previousDefinition);
+        }
+
+        producerCallbacks.restartEiJobs(producer, this.eiJobs);
+        consumerCallbacks.notifyConsumersProducerAdded(producer);
+        return producer;
+    }
+
+    private void purgeTypes(Collection<EiType> types) {
+        for (EiType type : types) {
+            if (getProducersForType(type.getId()).isEmpty()) {
+                this.eiTypes.remove(type);
+            }
+        }
+    }
+
+    private EiType getType(EiTypeRegistrationInfo typeInfo) {
+        EiType type = this.eiTypes.get(typeInfo.id());
+        if (type == null) {
+            type = new EiType(typeInfo.id(), typeInfo.jobDataSchema());
+            this.eiTypes.put(type);
+            this.consumerCallbacks.notifyConsumersTypeAdded(type);
+        }
+        return type;
+    }
+
+    private EiProducer createProducer(EiProducerRegistrationInfo producerInfo) {
+        ArrayList<EiType> types = new ArrayList<>();
+
+        EiProducer producer = new EiProducer(producerInfo.id(), types, producerInfo.jobCallbackUrl(),
+            producerInfo.producerSupervisionCallbackUrl());
+
+        for (EiTypeRegistrationInfo typeInfo : producerInfo.supportedTypes()) {
+            EiType type = getType(typeInfo);
+            types.add(type);
+        }
+        return producer;
     }
 
     public synchronized Collection<EiProducer> getAllProducers() {
@@ -58,33 +150,42 @@ public class EiProducers {
         return allEiProducers.get(id);
     }
 
-    public synchronized void remove(String id) {
-        this.allEiProducers.remove(id);
-    }
-
     public synchronized int size() {
         return allEiProducers.size();
     }
 
     public synchronized void clear() {
         this.allEiProducers.clear();
+        this.producersByType.clear();
     }
 
-    public void deregisterProducer(EiProducer producer, EiTypes eiTypes, EiJobs eiJobs) {
-        this.remove(producer);
+    public void deregisterProducer(EiProducer producer, EiTypes eiTypes) {
+        allEiProducers.remove(producer.getId());
         for (EiType type : producer.getEiTypes()) {
-            boolean removed = type.removeProducer(producer) != null;
-            if (!removed) {
+            if (producersByType.remove(type.getId(), producer.getId()) == null) {
                 this.logger.error("Bug, no producer found");
             }
-            if (type.getProducerIds().isEmpty()) {
+            if (this.producersByType.get(type.getId()).isEmpty()) {
                 eiTypes.remove(type);
             }
         }
+        this.consumerCallbacks.notifyConsumersProducerDeleted(producer);
     }
 
-    private synchronized void remove(EiProducer producer) {
-        this.allEiProducers.remove(producer.getId());
+    public synchronized Collection<EiProducer> getProducersForType(EiType type) {
+        return this.producersByType.get(type.getId());
+    }
+
+    public synchronized Collection<EiProducer> getProducersForType(String typeId) {
+        return this.producersByType.get(typeId);
+    }
+
+    public synchronized Collection<String> getProducerIdsForType(String typeId) {
+        Collection<String> producerIds = new ArrayList<>();
+        for (EiProducer p : this.getProducersForType(typeId)) {
+            producerIds.add(p.getId());
+        }
+        return producerIds;
     }
 
 }
index a354198..5d9057a 100644 (file)
 
 package org.oransc.enrichment.repository;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
 import lombok.Getter;
 
 public class EiType {
@@ -34,26 +29,9 @@ public class EiType {
     @Getter
     private final Object jobDataSchema;
 
-    private final Map<String, EiProducer> producers = new HashMap<>();
-
     public EiType(String id, Object jobDataSchema) {
         this.id = id;
         this.jobDataSchema = jobDataSchema;
     }
 
-    public synchronized Collection<EiProducer> getProducers() {
-        return Collections.unmodifiableCollection(producers.values());
-    }
-
-    public synchronized Collection<String> getProducerIds() {
-        return Collections.unmodifiableCollection(producers.keySet());
-    }
-
-    public synchronized void addProducer(EiProducer producer) {
-        this.producers.put(producer.getId(), producer);
-    }
-
-    public synchronized EiProducer removeProducer(EiProducer producer) {
-        return this.producers.remove(producer.getId());
-    }
 }
index d0bf53a..265a8e4 100644 (file)
 
 package org.oransc.enrichment.repository;
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapterFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
 import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.ServiceLoader;
 import java.util.Vector;
 
+import org.oransc.enrichment.configuration.ApplicationConfig;
 import org.oransc.enrichment.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.FileSystemUtils;
 
 /**
  * Dynamic representation of all EI types in the system.
@@ -37,9 +51,30 @@ import org.slf4j.LoggerFactory;
 public class EiTypes {
     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
     private final Map<String, EiType> allEiTypes = new HashMap<>();
+    private final ApplicationConfig config;
+    private final Gson gson;
+
+    public EiTypes(ApplicationConfig config) {
+        this.config = config;
+        GsonBuilder gsonBuilder = new GsonBuilder();
+        ServiceLoader.load(TypeAdapterFactory.class).forEach(gsonBuilder::registerTypeAdapterFactory);
+        this.gson = gsonBuilder.create();
+    }
+
+    public synchronized void restoreTypesFromDatabase() throws IOException {
+        Files.createDirectories(Paths.get(getDatabaseDirectory()));
+        File dbDir = new File(getDatabaseDirectory());
+
+        for (File file : dbDir.listFiles()) {
+            String json = Files.readString(file.toPath());
+            EiType type = gson.fromJson(json, EiType.class);
+            allEiTypes.put(type.getId(), type);
+        }
+    }
 
     public synchronized void put(EiType type) {
         allEiTypes.put(type.getId(), type);
+        storeInFile(type);
     }
 
     public synchronized Collection<EiType> getAllEiTypes() {
@@ -58,12 +93,13 @@ public class EiTypes {
         return allEiTypes.get(id);
     }
 
-    public synchronized void remove(String id) {
-        allEiTypes.remove(id);
-    }
-
     public synchronized void remove(EiType type) {
-        this.remove(type.getId());
+        allEiTypes.remove(type.getId());
+        try {
+            Files.delete(getPath(type));
+        } catch (IOException e) {
+            logger.warn("Could not remove file: {} {}", type.getId(), e.getMessage());
+        }
     }
 
     public synchronized int size() {
@@ -72,5 +108,41 @@ public class EiTypes {
 
     public synchronized void clear() {
         this.allEiTypes.clear();
+        clearDatabase();
+    }
+
+    private void clearDatabase() {
+        try {
+            FileSystemUtils.deleteRecursively(Path.of(getDatabaseDirectory()));
+            Files.createDirectories(Paths.get(getDatabaseDirectory()));
+        } catch (IOException e) {
+            logger.warn("Could not delete database : {}", e.getMessage());
+        }
+    }
+
+    private void storeInFile(EiType type) {
+        try {
+            try (PrintStream out = new PrintStream(new FileOutputStream(getFile(type)))) {
+                out.print(gson.toJson(type));
+            }
+        } catch (Exception e) {
+            logger.warn("Could not save job: {} {}", type.getId(), e.getMessage());
+        }
+    }
+
+    private File getFile(EiType type) {
+        return getPath(type).toFile();
+    }
+
+    private Path getPath(EiType type) {
+        return getPath(type.getId());
+    }
+
+    private Path getPath(String typeId) {
+        return Path.of(getDatabaseDirectory(), typeId);
+    }
+
+    private String getDatabaseDirectory() {
+        return config.getVardataDirectory() + "/eitypes";
     }
 }
index c2b8270..25e559c 100644 (file)
@@ -38,14 +38,16 @@ public class MultiMap<T> {
         this.map.computeIfAbsent(key, k -> new HashMap<>()).put(id, value);
     }
 
-    public void remove(String key, String id) {
+    public T remove(String key, String id) {
         Map<String, T> innerMap = this.map.get(key);
         if (innerMap != null) {
-            innerMap.remove(id);
+            T removedElement = innerMap.remove(id);
             if (innerMap.isEmpty()) {
                 this.map.remove(key);
             }
+            return removedElement;
         }
+        return null;
     }
 
     public Collection<T> get(String key) {
index b4c21d4..d8ee970 100644 (file)
@@ -89,7 +89,7 @@ public class ProducerSupervision {
         logger.warn("Unresponsive producer: {} exception: {}", producer.getId(), throwable.getMessage());
         producer.setAliveStatus(false);
         if (producer.isDead()) {
-            this.eiProducers.deregisterProducer(producer, this.eiTypes, this.eiJobs);
+            this.eiProducers.deregisterProducer(producer, this.eiTypes);
             this.consumerCallbacks.notifyConsumersProducerDeleted(producer);
         }
     }
index b62a965..9f3dd49 100644 (file)
@@ -55,6 +55,7 @@ import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
 import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
+import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
 import org.oransc.enrichment.controllers.producer.ProducerConsts;
 import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
@@ -127,6 +128,9 @@ class ApplicationTest {
     @Autowired
     ProducerSupervision producerSupervision;
 
+    @Autowired
+    ProducerCallbacks producerCallbacks;
+
     private static Gson gson = new GsonBuilder().create();
 
     /**
@@ -382,7 +386,7 @@ class ApplicationTest {
 
         assertThat(this.eiTypes.size()).isEqualTo(1);
         EiType type = this.eiTypes.getType(EI_TYPE_ID);
-        assertThat(type.getProducerIds()).contains("eiProducerId");
+        assertThat(this.eiProducers.getProducersForType(EI_TYPE_ID).size()).isEqualTo(1);
         assertThat(this.eiProducers.size()).isEqualTo(1);
         assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
 
@@ -453,14 +457,14 @@ class ApplicationTest {
 
         assertThat(this.eiProducers.size()).isEqualTo(2);
         EiType type = this.eiTypes.getType(EI_TYPE_ID);
-        assertThat(type.getProducerIds()).contains("eiProducerId");
-        assertThat(type.getProducerIds()).contains("eiProducerId2");
+        assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
+        assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
         putEiJob(EI_TYPE_ID, "jobId");
         assertThat(this.eiJobs.size()).isEqualTo(1);
 
         deleteEiProducer("eiProducerId");
         assertThat(this.eiProducers.size()).isEqualTo(1);
-        assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds()).doesNotContain("eiProducerId");
+        assertThat(this.eiProducers.getProducerIdsForType(EI_TYPE_ID)).doesNotContain("eiProducerId");
         verifyJobStatus("jobId", "ENABLED");
 
         deleteEiProducer("eiProducerId2");
@@ -555,8 +559,8 @@ class ApplicationTest {
 
         // After 3 failed checks, the producer and the type shall be deregisterred
         this.producerSupervision.createTask().blockLast();
-        assertThat(this.eiProducers.size()).isEqualTo(0);
-        assertThat(this.eiTypes.size()).isEqualTo(0);
+        assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
+        assertThat(this.eiTypes.size()).isEqualTo(0); // The type is removed
         verifyJobStatus(EI_JOB_ID, "DISABLED");
 
         // Job disabled status notification shall be received
@@ -585,21 +589,49 @@ class ApplicationTest {
 
         {
             // Restore the jobs
-            EiJobs jobs = new EiJobs(this.applicationConfig);
+            EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
             jobs.restoreJobsFromDatabase();
             assertThat(jobs.size()).isEqualTo(2);
-            jobs.remove("jobId1");
-            jobs.remove("jobId2");
+            jobs.remove("jobId1", this.eiProducers);
+            jobs.remove("jobId2", this.eiProducers);
         }
         {
             // Restore the jobs, no jobs in database
-            EiJobs jobs = new EiJobs(this.applicationConfig);
+            EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
             jobs.restoreJobsFromDatabase();
             assertThat(jobs.size()).isEqualTo(0);
         }
         logger.warn("Test removing a job when the db file is gone");
-        this.eiJobs.remove("jobId1");
+        this.eiJobs.remove("jobId1", this.eiProducers);
         assertThat(this.eiJobs.size()).isEqualTo(1);
+
+        ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
+        await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
+    }
+
+    @Test
+    void testEiTypesDatabase() throws Exception {
+        putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
+
+        assertThat(this.eiTypes.size()).isEqualTo(1);
+
+        {
+            // Restore the types
+            EiTypes types = new EiTypes(this.applicationConfig);
+            types.restoreTypesFromDatabase();
+            assertThat(types.size()).isEqualTo(1);
+
+        }
+        {
+            // Restore the jobs, no jobs in database
+            EiTypes types = new EiTypes(this.applicationConfig);
+            types.clear();
+            types.restoreTypesFromDatabase();
+            assertThat(types.size()).isEqualTo(0);
+        }
+        logger.warn("Test removing a job when the db file is gone");
+        this.eiTypes.remove(this.eiTypes.getType(EI_TYPE_ID));
+        assertThat(this.eiJobs.size()).isEqualTo(0);
     }
 
     private void deleteEiProducer(String eiProducerId) {
@@ -706,6 +738,7 @@ class ApplicationTest {
         String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
 
         restClient().putForEntity(url, body).block();
+
         return this.eiTypes.getType(eiTypeId);
     }
 
diff --git a/r-app-catalogue/eclipse-formatter.xml b/r-app-catalogue/eclipse-formatter.xml
new file mode 100644 (file)
index 0000000..c8cca2e
--- /dev/null
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * ========================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===================================
+  -->
+<profiles version="13">
+<profile kind="CodeFormatterProfile" name="java-formatter" version="12">
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+</profile>
+</profiles>
index a2e0565..07e4c39 100644 (file)
@@ -45,6 +45,8 @@
         <jackson-databind-nullable.version>0.2.1</jackson-databind-nullable.version>\r
         <openapi-generator-maven-plugin.version>4.3.1</openapi-generator-maven-plugin.version>\r
         <swagger-codegen-maven-plugin.version>3.0.11</swagger-codegen-maven-plugin.version>\r
+        <formatter-maven-plugin.version>2.12.2</formatter-maven-plugin.version>\r
+        <spotless-maven-plugin.version>1.24.3</spotless-maven-plugin.version>\r
         <jacoco-maven-plugin.version>0.8.6</jacoco-maven-plugin.version>\r
         <docker-maven-plugin.version>0.30.0</docker-maven-plugin.version>\r
     </properties>\r
                             <invokerPackage>org.oransc.rappcatalogue</invokerPackage>\r
                             <configOptions>\r
                                 <delegatePattern>true</delegatePattern>\r
+                                <unhandledException>true</unhandledException>\r
                             </configOptions>\r
                         </configuration>\r
                     </execution>\r
                     </execution>\r
                 </executions>\r
             </plugin>\r
+            <plugin>\r
+                <groupId>net.revelc.code.formatter</groupId>\r
+                <artifactId>formatter-maven-plugin</artifactId>\r
+                <version>${formatter-maven-plugin.version}</version>\r
+                <configuration>\r
+                    <configFile>${project.basedir}/eclipse-formatter.xml</configFile>\r
+                </configuration>\r
+                <!-- https://code.revelc.net/formatter-maven-plugin/ use mvn formatter:format\r
+                    spotless:apply process-sources -->\r
+            </plugin>\r
+            <plugin>\r
+                <groupId>com.diffplug.spotless</groupId>\r
+                <artifactId>spotless-maven-plugin</artifactId>\r
+                <version>${spotless-maven-plugin.version}</version>\r
+                <configuration>\r
+                    <java>\r
+                        <removeUnusedImports />\r
+                        <importOrder>\r
+                            <order>com,java,javax,org</order>\r
+                        </importOrder>\r
+                    </java>\r
+                </configuration>\r
+                <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven use\r
+                    mvn spotless:apply to rewrite source files use mvn spotless:check to validate\r
+                    source files -->\r
+            </plugin>\r
             <plugin>\r
                 <groupId>org.jacoco</groupId>\r
                 <artifactId>jacoco-maven-plugin</artifactId>\r
index 939f7bf..072a5a0 100644 (file)
@@ -35,22 +35,19 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep
 @ControllerAdvice
 public class GeneralRappCatalogueControllerAdvisor extends ResponseEntityExceptionHandler {
     @ExceptionHandler(InvalidServiceException.class)
-    public ResponseEntity<Object> handleInvalidServiceException(
-        InvalidServiceException ex) {
+    public ResponseEntity<Object> handleInvalidServiceException(InvalidServiceException ex) {
 
         return new ResponseEntity<>(getErrorInformation(ex, BAD_REQUEST), BAD_REQUEST);
     }
 
     @ExceptionHandler(ServiceNotFoundException.class)
-    public ResponseEntity<Object> handleServiceNotFoundException(
-        ServiceNotFoundException ex) {
+    public ResponseEntity<Object> handleServiceNotFoundException(ServiceNotFoundException ex) {
 
         return new ResponseEntity<>(getErrorInformation(ex, NOT_FOUND), NOT_FOUND);
     }
 
     @ExceptionHandler(HeaderException.class)
-    public ResponseEntity<Object> handleHeaderException(
-        HeaderException ex) {
+    public ResponseEntity<Object> handleHeaderException(HeaderException ex) {
 
         return new ResponseEntity<>(getErrorInformation(ex, INTERNAL_SERVER_ERROR), INTERNAL_SERVER_ERROR);
     }
index bb3a6dc..4615d69 100644 (file)
-/*-\r
- * ========================LICENSE_START=================================\r
- * Copyright (C) 2020 Nordix Foundation. All rights reserved.\r
- * ======================================================================\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *      http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- * ========================LICENSE_END===================================\r
- */\r
-\r
-package org.oransc.rappcatalogue.api;\r
-\r
-import java.io.IOException;\r
-import java.sql.Date;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.Optional;\r
-import java.util.concurrent.ConcurrentHashMap;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-import org.oransc.rappcatalogue.exception.HeaderException;\r
-import org.oransc.rappcatalogue.exception.InvalidServiceException;\r
-import org.oransc.rappcatalogue.exception.ServiceNotFoundException;\r
-import org.oransc.rappcatalogue.model.InputService;\r
-import org.oransc.rappcatalogue.model.Service;\r
-import org.springframework.beans.factory.annotation.Autowired;\r
-import org.springframework.http.HttpStatus;\r
-import org.springframework.http.ResponseEntity;\r
-import org.springframework.web.context.request.NativeWebRequest;\r
-\r
-@org.springframework.stereotype.Service\r
-public class ServicesApiDelegateImpl implements ServicesApiDelegate {\r
-\r
-    private static final String LOCATION_HEADER = "Location";\r
-\r
-    @Autowired\r
-    private NativeWebRequest nativeWebRequest;\r
-\r
-    private ConcurrentHashMap<String, Service> registeredServices = new ConcurrentHashMap<>();\r
-\r
-    ServicesApiDelegateImpl(NativeWebRequest nativeWebRequest) {\r
-        this.nativeWebRequest = nativeWebRequest;\r
-    }\r
-\r
-    @Override\r
-    public Optional<NativeWebRequest> getRequest() {\r
-        return Optional.of(nativeWebRequest);\r
-    }\r
-\r
-    @Override\r
-    public ResponseEntity<Service> getIndividualService(String serviceName) {\r
-        Service service = registeredServices.get(serviceName);\r
-        if (service != null) {\r
-            return ResponseEntity.ok(service);\r
-        } else {\r
-            throw new ServiceNotFoundException(serviceName);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public ResponseEntity<List<Service>> getServices() {\r
-        return ResponseEntity.ok(new ArrayList<>(registeredServices.values()));\r
-    }\r
-\r
-    @Override\r
-    public ResponseEntity<Void> putIndividualService(String serviceName, InputService inputService) {\r
-        if (isServiceValid(inputService)) {\r
-            if (registeredServices.put(serviceName, createService(serviceName, inputService)) == null) {\r
-                try {\r
-                    getRequest().ifPresent(request -> addLocationHeaderToResponse(serviceName, request));\r
-                } catch (Exception e) {\r
-                    registeredServices.remove(serviceName);\r
-                    throw e;\r
-                }\r
-                return new ResponseEntity<>(HttpStatus.CREATED);\r
-            } else {\r
-                return new ResponseEntity<>(HttpStatus.OK);\r
-            }\r
-        } else {\r
-            throw new InvalidServiceException();\r
-        }\r
-    }\r
-\r
-    private void addLocationHeaderToResponse(String serviceName, NativeWebRequest request) {\r
-        try {\r
-            HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);\r
-            HttpServletResponse nativeResponse = request.getNativeResponse(HttpServletResponse.class);\r
-            if (nativeRequest != null && nativeResponse != null) {\r
-                StringBuffer requestURL = nativeRequest.getRequestURL();\r
-                nativeResponse.addHeader(LOCATION_HEADER, requestURL.toString());\r
-                nativeResponse.getWriter().print("");\r
-            } else {\r
-                throw new HeaderException(LOCATION_HEADER, serviceName,\r
-                    new Exception("Native Request or Response missing"));\r
-            }\r
-        } catch (IOException e) {\r
-            throw new HeaderException(LOCATION_HEADER, serviceName, e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public ResponseEntity<Void> deleteIndividualService(String serviceName) {\r
-        registeredServices.remove(serviceName);\r
-        return new ResponseEntity<>(HttpStatus.NO_CONTENT);\r
-    }\r
-\r
-    /*\r
-     * java:S2589: Boolean expressions should not be gratuitous.\r
-     * Even though the version property is marked as @NotNull, it might be null coming from the client, hence the null\r
-     * check is needed.\r
-     */\r
-    @SuppressWarnings("java:S2589")\r
-    private boolean isServiceValid(InputService service) {\r
-        String version = service.getVersion();\r
-        return version != null && !version.isBlank();\r
-    }\r
-\r
-    private Service createService(String serviceName, InputService inputService) {\r
-        Service service = new Service();\r
-        service.setName(serviceName);\r
-        service.setDescription(inputService.getDescription());\r
-        service.setDisplayName(inputService.getDisplayName());\r
-        service.setVersion(inputService.getVersion());\r
-        service.setRegistrationDate(getTodaysDate());\r
-        return service;\r
-    }\r
-\r
-    private String getTodaysDate() {\r
-        long millis = System.currentTimeMillis();\r
-        Date date = new Date(millis);\r
-        return date.toString();\r
-    }\r
-}\r
+/*-
+ * ========================LICENSE_START=================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * 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.rappcatalogue.api;
+
+import java.io.IOException;
+import java.sql.Date;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.oransc.rappcatalogue.exception.HeaderException;
+import org.oransc.rappcatalogue.exception.InvalidServiceException;
+import org.oransc.rappcatalogue.exception.ServiceNotFoundException;
+import org.oransc.rappcatalogue.model.InputService;
+import org.oransc.rappcatalogue.model.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.request.NativeWebRequest;
+
+@org.springframework.stereotype.Service
+public class ServicesApiDelegateImpl implements ServicesApiDelegate {
+
+    private static final String LOCATION_HEADER = "Location";
+
+    @Autowired
+    private NativeWebRequest nativeWebRequest;
+
+    private ConcurrentHashMap<String, Service> registeredServices = new ConcurrentHashMap<>();
+
+    ServicesApiDelegateImpl(NativeWebRequest nativeWebRequest) {
+        this.nativeWebRequest = nativeWebRequest;
+    }
+
+    @Override
+    public Optional<NativeWebRequest> getRequest() {
+        return Optional.of(nativeWebRequest);
+    }
+
+    @Override
+    public ResponseEntity<Service> getIndividualService(String serviceName) throws ServiceNotFoundException {
+        Service service = registeredServices.get(serviceName);
+        if (service != null) {
+            return ResponseEntity.ok(service);
+        } else {
+            throw new ServiceNotFoundException(serviceName);
+        }
+    }
+
+    @Override
+    public ResponseEntity<List<Service>> getServices() {
+        return ResponseEntity.ok(new ArrayList<>(registeredServices.values()));
+    }
+
+    @Override
+    public ResponseEntity<Void> putIndividualService(String serviceName, InputService inputService)
+        throws InvalidServiceException, HeaderException {
+        if (isServiceValid(inputService)) {
+            if (registeredServices.put(serviceName, createService(serviceName, inputService)) == null) {
+                try {
+                    Optional<NativeWebRequest> request = getRequest();
+                    if (request.isPresent()) {
+                        addLocationHeaderToResponse(serviceName, request.get());
+                    }
+                } catch (HeaderException e) {
+                    registeredServices.remove(serviceName);
+                    throw e;
+                }
+                return new ResponseEntity<>(HttpStatus.CREATED);
+            } else {
+                return new ResponseEntity<>(HttpStatus.OK);
+            }
+        } else {
+            throw new InvalidServiceException();
+        }
+    }
+
+    private void addLocationHeaderToResponse(String serviceName, NativeWebRequest request) throws HeaderException {
+        try {
+            HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);
+            HttpServletResponse nativeResponse = request.getNativeResponse(HttpServletResponse.class);
+            if (nativeRequest != null && nativeResponse != null) {
+                StringBuffer requestURL = nativeRequest.getRequestURL();
+                nativeResponse.addHeader(LOCATION_HEADER, requestURL.toString());
+                nativeResponse.getWriter().print("");
+            } else {
+                throw new HeaderException(LOCATION_HEADER, serviceName,
+                    new Exception("Native Request or Response missing"));
+            }
+        } catch (IOException e) {
+            throw new HeaderException(LOCATION_HEADER, serviceName, e);
+        }
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteIndividualService(String serviceName) {
+        registeredServices.remove(serviceName);
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    /*
+     * java:S2589: Boolean expressions should not be gratuitous. Even though the
+     * version property is marked as @NotNull, it might be null coming from the
+     * client, hence the null check is needed.
+     */
+    @SuppressWarnings("java:S2589")
+    private boolean isServiceValid(InputService service) {
+        String version = service.getVersion();
+        return version != null && !version.isBlank();
+    }
+
+    private Service createService(String serviceName, InputService inputService) {
+        Service service = new Service();
+        service.setName(serviceName);
+        service.setDescription(inputService.getDescription());
+        service.setDisplayName(inputService.getDisplayName());
+        service.setVersion(inputService.getVersion());
+        service.setRegistrationDate(getTodaysDate());
+        return service;
+    }
+
+    private String getTodaysDate() {
+        long millis = System.currentTimeMillis();
+        Date date = new Date(millis);
+        return date.toString();
+    }
+}
index 676ae1c..8f64449 100644 (file)
@@ -18,7 +18,7 @@
 
 package org.oransc.rappcatalogue.exception;
 
-public class HeaderException extends RuntimeException {
+public class HeaderException extends Exception {
 
     private static final long serialVersionUID = -7798178963078284655L;
 
index dce815b..45ec769 100644 (file)
@@ -18,7 +18,7 @@
 
 package org.oransc.rappcatalogue.exception;
 
-public class InvalidServiceException extends RuntimeException {
+public class InvalidServiceException extends Exception {
     private static final long serialVersionUID = 3849219105170316564L;
 
     public InvalidServiceException() {
index 26b4b27..8411cf4 100644 (file)
@@ -18,7 +18,7 @@
 
 package org.oransc.rappcatalogue.exception;
 
-public class ServiceNotFoundException extends RuntimeException {
+public class ServiceNotFoundException extends Exception {
     private static final long serialVersionUID = 6579271315716003988L;
 
     public ServiceNotFoundException(String serviceName) {
index f2f30ff..dd10a65 100644 (file)
-package org.oransc.rappcatalogue.api;\r
-\r
-import static org.assertj.core.api.Assertions.assertThat;\r
-import static org.junit.jupiter.api.Assertions.assertThrows;\r
-import static org.mockito.Mockito.mock;\r
-import static org.mockito.Mockito.verify;\r
-import static org.mockito.Mockito.when;\r
-import static org.springframework.http.HttpStatus.CREATED;\r
-import static org.springframework.http.HttpStatus.NO_CONTENT;\r
-import static org.springframework.http.HttpStatus.OK;\r
-\r
-import java.io.IOException;\r
-import java.io.PrintWriter;\r
-import java.sql.Date;\r
-import java.util.Arrays;\r
-import java.util.List;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-import org.junit.jupiter.api.Test;\r
-import org.junit.jupiter.api.extension.ExtendWith;\r
-import org.mockito.Mock;\r
-import org.mockito.junit.jupiter.MockitoExtension;\r
-import org.oransc.rappcatalogue.exception.HeaderException;\r
-import org.oransc.rappcatalogue.exception.InvalidServiceException;\r
-import org.oransc.rappcatalogue.exception.ServiceNotFoundException;\r
-import org.oransc.rappcatalogue.model.InputService;\r
-import org.oransc.rappcatalogue.model.Service;\r
-import org.springframework.http.ResponseEntity;\r
-import org.springframework.web.context.request.NativeWebRequest;\r
-\r
-@ExtendWith(MockitoExtension.class)\r
-class ServicesApiDelegateImplTest {\r
-\r
-    @Mock\r
-    NativeWebRequest webRequestMock;\r
-\r
-    private static final String INVALID_SERVICE_MESSAGE = "Service is missing required property: version";\r
-    private static final String SERVICE_NAME = "Service Name";\r
-    private static final String SERVICE_DESCRIPTION = "description";\r
-    private static final String SERVICE_VERSION = "1.0";\r
-    private static final String SERVICE_DISPLAY_NAME = "Display Name";\r
-\r
-    @Test\r
-    void getAddedService_shouldReturnService() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        InputService service = new InputService();\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setVersion(SERVICE_VERSION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        whenPrintResponseShouldWork();\r
-\r
-        delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-\r
-        ResponseEntity<Service> response = delegateUnderTest.getIndividualService(SERVICE_NAME);\r
-\r
-        assertThat(response.getStatusCode()).isEqualTo(OK);\r
-        assertThat(response.getBody().getName()).isEqualTo(SERVICE_NAME);\r
-    }\r
-\r
-    @Test\r
-    void getMissingService_shouldThrowException() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);\r
-\r
-        Exception exception = assertThrows(ServiceNotFoundException.class, () -> {\r
-            delegateUnderTest.getIndividualService(SERVICE_NAME);\r
-        });\r
-\r
-        String expectedMessage = "Service " + SERVICE_NAME + " not found";\r
-        String actualMessage = exception.getMessage();\r
-\r
-        assertThat(actualMessage).isEqualTo(expectedMessage);\r
-    }\r
-\r
-    @Test\r
-    void putNewValidService_shouldBeCreatedAndRegisteredAndUrlToNewServiceAddedToLocationHeaderInResponse() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        InputService service = new InputService();\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setVersion(SERVICE_VERSION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        String urlToCreatedService = "URL to created Service";\r
-        HttpServletResponse servletResponseMock = whenPrintResponseShouldWork(urlToCreatedService);\r
-\r
-        ResponseEntity<Void> putResponse = delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-\r
-        assertThat(putResponse.getStatusCode()).isEqualTo(CREATED);\r
-        verify(servletResponseMock).addHeader("Location", urlToCreatedService);\r
-\r
-        ResponseEntity<Service> getResponse = delegateUnderTest.getIndividualService(SERVICE_NAME);\r
-\r
-        assertThat(getResponse.getStatusCode()).isEqualTo(OK);\r
-        Service body = getResponse.getBody();\r
-        assertThat(body.getName()).isEqualTo(SERVICE_NAME);\r
-        assertThat(body.getRegistrationDate()).isEqualTo(getTodaysDate());\r
-    }\r
-\r
-    @Test\r
-    void putModifiedService_shouldBeModified() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        InputService service = new InputService();\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setVersion(SERVICE_VERSION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        whenPrintResponseShouldWork();\r
-\r
-        delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-\r
-        String newDescription = "New description";\r
-        service.setDescription(newDescription);\r
-        ResponseEntity<Void> putResponse = delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-\r
-        assertThat(putResponse.getStatusCode()).isEqualTo(OK);\r
-\r
-        ResponseEntity<Service> getResponse = delegateUnderTest.getIndividualService(SERVICE_NAME);\r
-\r
-        assertThat(getResponse.getStatusCode()).isEqualTo(OK);\r
-        assertThat(getResponse.getBody().getDescription()).isEqualTo(newDescription);\r
-    }\r
-\r
-    @Test\r
-    void putServiceWithVersionNull_shouldThrowException() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);\r
-\r
-        InputService service = new InputService();\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        Exception exception = assertThrows(InvalidServiceException.class, () -> {\r
-            delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-        });\r
-\r
-        assertThat(exception.getMessage()).isEqualTo(INVALID_SERVICE_MESSAGE);\r
-    }\r
-\r
-    @Test\r
-    void putServiceWithBlankVersion_shouldThrowException() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);\r
-\r
-        InputService service = new InputService();\r
-        service.setVersion("");\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        Exception exception = assertThrows(InvalidServiceException.class, () -> {\r
-            delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-        });\r
-\r
-        assertThat(exception.getMessage()).isEqualTo(INVALID_SERVICE_MESSAGE);\r
-    }\r
-\r
-    @Test\r
-    void putServiceWhenIoExceptionAddingHeader_shouldThrowExceptionAndNoServiceCreated() throws Exception {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        whenGetRequestUrlThenReturnUrl();\r
-        HttpServletResponse servletResponseMock = mock(HttpServletResponse.class);\r
-        when(webRequestMock.getNativeResponse(HttpServletResponse.class)).thenReturn(servletResponseMock);\r
-        when(servletResponseMock.getWriter()).thenThrow(new IOException("Error"));\r
-\r
-        InputService service = new InputService();\r
-        service.setVersion("1.0");\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        Exception exception = assertThrows(HeaderException.class, () -> {\r
-            delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-        });\r
-\r
-        assertThat(exception.getMessage())\r
-            .isEqualTo("Unable to set header Location in put response for service " + SERVICE_NAME + ". Cause: Error");\r
-\r
-        ResponseEntity<List<Service>> response = delegateUnderTest.getServices();\r
-        assertThat(response.getBody()).isEmpty();\r
-    }\r
-\r
-    @Test\r
-    void getServices_shouldProvideArrayOfAddedServiceNames() throws Exception {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        InputService service1 = new InputService();\r
-        service1.setDescription("description 1");\r
-        service1.setVersion(SERVICE_VERSION);\r
-        service1.setDisplayName("Display Name 1");\r
-\r
-        InputService service2 = new InputService();\r
-        service2.setDescription("description 2");\r
-        service2.setVersion(SERVICE_VERSION);\r
-        service2.setDisplayName("Display Name 2");\r
-\r
-        whenPrintResponseShouldWork();\r
-\r
-        String serviceName1 = "Service Name 1";\r
-        delegateUnderTest.putIndividualService(serviceName1, service1);\r
-        String serviceName2 = "Service Name 2";\r
-        delegateUnderTest.putIndividualService(serviceName2, service2);\r
-\r
-        ResponseEntity<List<Service>> response = delegateUnderTest.getServices();\r
-\r
-        assertThat(response.getStatusCode()).isEqualTo(OK);\r
-        List<Service> services = response.getBody();\r
-        assertThat(services).hasSize(2);\r
-        List<String> expectedServiceNames = Arrays.asList(serviceName1, serviceName2);\r
-        assertThat(expectedServiceNames).contains(services.get(0).getName()) //\r
-            .contains(services.get(1).getName());\r
-    }\r
-\r
-    @Test\r
-    void deleteService_shouldBeOk() {\r
-        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);\r
-\r
-        InputService service = new InputService();\r
-        service.setDescription(SERVICE_DESCRIPTION);\r
-        service.setVersion(SERVICE_VERSION);\r
-        service.setDisplayName(SERVICE_DISPLAY_NAME);\r
-\r
-        whenPrintResponseShouldWork();\r
-\r
-        delegateUnderTest.putIndividualService(SERVICE_NAME, service);\r
-\r
-        ResponseEntity<List<Service>> servicesResponse = delegateUnderTest.getServices();\r
-\r
-        assertThat(servicesResponse.getBody()).hasSize(1);\r
-\r
-        ResponseEntity<Void> deleteResponse = delegateUnderTest.deleteIndividualService(SERVICE_NAME);\r
-\r
-        assertThat(deleteResponse.getStatusCode()).isEqualTo(NO_CONTENT);\r
-\r
-        servicesResponse = delegateUnderTest.getServices();\r
-\r
-        assertThat(servicesResponse.getBody()).isEmpty();\r
-    }\r
-\r
-    private void whenGetRequestUrlThenReturnUrl() {\r
-        whenGetRequestUrlThenReturnUrl("URL");\r
-    }\r
-\r
-    private void whenGetRequestUrlThenReturnUrl(String url) {\r
-        HttpServletRequest servletRequestMock = mock(HttpServletRequest.class);\r
-        when(webRequestMock.getNativeRequest(HttpServletRequest.class)).thenReturn(servletRequestMock);\r
-        when(servletRequestMock.getRequestURL()).thenReturn(new StringBuffer(url));\r
-    }\r
-\r
-    private HttpServletResponse whenPrintResponseShouldWork() {\r
-        return whenPrintResponseShouldWork("URL");\r
-    }\r
-\r
-    private HttpServletResponse whenPrintResponseShouldWork(String url) {\r
-        whenGetRequestUrlThenReturnUrl(url);\r
-        HttpServletResponse servletResponseMock = mock(HttpServletResponse.class);\r
-        when(webRequestMock.getNativeResponse(HttpServletResponse.class)).thenReturn(servletResponseMock);\r
-        PrintWriter printWriterMock = mock(PrintWriter.class);\r
-        try {\r
-            when(servletResponseMock.getWriter()).thenReturn(printWriterMock);\r
-        } catch (IOException e) {\r
-            // Nothing\r
-        }\r
-        return servletResponseMock;\r
-    }\r
-\r
-    private String getTodaysDate() {\r
-        long millis = System.currentTimeMillis();\r
-        Date date = new Date(millis);\r
-        return date.toString();\r
-    }\r
-}\r
+
+package org.oransc.rappcatalogue.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.NO_CONTENT;
+import static org.springframework.http.HttpStatus.OK;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Date;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.oransc.rappcatalogue.exception.HeaderException;
+import org.oransc.rappcatalogue.exception.InvalidServiceException;
+import org.oransc.rappcatalogue.exception.ServiceNotFoundException;
+import org.oransc.rappcatalogue.model.InputService;
+import org.oransc.rappcatalogue.model.Service;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.request.NativeWebRequest;
+
+@ExtendWith(MockitoExtension.class)
+class ServicesApiDelegateImplTest {
+
+    @Mock
+    NativeWebRequest webRequestMock;
+
+    private static final String INVALID_SERVICE_MESSAGE = "Service is missing required property: version";
+    private static final String SERVICE_NAME = "Service Name";
+    private static final String SERVICE_DESCRIPTION = "description";
+    private static final String SERVICE_VERSION = "1.0";
+    private static final String SERVICE_DISPLAY_NAME = "Display Name";
+
+    @Test
+    void getAddedService_shouldReturnService() throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        InputService service = new InputService();
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setVersion(SERVICE_VERSION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        whenPrintResponseShouldWork();
+
+        delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+
+        ResponseEntity<Service> response = delegateUnderTest.getIndividualService(SERVICE_NAME);
+
+        assertThat(response.getStatusCode()).isEqualTo(OK);
+        assertThat(response.getBody().getName()).isEqualTo(SERVICE_NAME);
+    }
+
+    @Test
+    void getMissingService_shouldThrowException() {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);
+
+        Exception exception = assertThrows(ServiceNotFoundException.class, () -> {
+            delegateUnderTest.getIndividualService(SERVICE_NAME);
+        });
+
+        String expectedMessage = "Service " + SERVICE_NAME + " not found";
+        String actualMessage = exception.getMessage();
+
+        assertThat(actualMessage).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    void putNewValidService_shouldBeCreatedAndRegisteredAndUrlToNewServiceAddedToLocationHeaderInResponse()
+        throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        InputService service = new InputService();
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setVersion(SERVICE_VERSION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        String urlToCreatedService = "URL to created Service";
+        HttpServletResponse servletResponseMock = whenPrintResponseShouldWork(urlToCreatedService);
+
+        ResponseEntity<Void> putResponse = delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+
+        assertThat(putResponse.getStatusCode()).isEqualTo(CREATED);
+        verify(servletResponseMock).addHeader("Location", urlToCreatedService);
+
+        ResponseEntity<Service> getResponse = delegateUnderTest.getIndividualService(SERVICE_NAME);
+
+        assertThat(getResponse.getStatusCode()).isEqualTo(OK);
+        Service body = getResponse.getBody();
+        assertThat(body.getName()).isEqualTo(SERVICE_NAME);
+        assertThat(body.getRegistrationDate()).isEqualTo(getTodaysDate());
+    }
+
+    @Test
+    void putModifiedService_shouldBeModified() throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        InputService service = new InputService();
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setVersion(SERVICE_VERSION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        whenPrintResponseShouldWork();
+
+        delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+
+        String newDescription = "New description";
+        service.setDescription(newDescription);
+        ResponseEntity<Void> putResponse = delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+
+        assertThat(putResponse.getStatusCode()).isEqualTo(OK);
+
+        ResponseEntity<Service> getResponse = delegateUnderTest.getIndividualService(SERVICE_NAME);
+
+        assertThat(getResponse.getStatusCode()).isEqualTo(OK);
+        assertThat(getResponse.getBody().getDescription()).isEqualTo(newDescription);
+    }
+
+    @Test
+    void putServiceWithVersionNull_shouldThrowException() {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);
+
+        InputService service = new InputService();
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        Exception exception = assertThrows(InvalidServiceException.class, () -> {
+            delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+        });
+
+        assertThat(exception.getMessage()).isEqualTo(INVALID_SERVICE_MESSAGE);
+    }
+
+    @Test
+    void putServiceWithBlankVersion_shouldThrowException() {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(null);
+
+        InputService service = new InputService();
+        service.setVersion("");
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        Exception exception = assertThrows(InvalidServiceException.class, () -> {
+            delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+        });
+
+        assertThat(exception.getMessage()).isEqualTo(INVALID_SERVICE_MESSAGE);
+    }
+
+    @Test
+    void putServiceWhenIoExceptionAddingHeader_shouldThrowExceptionAndNoServiceCreated() throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        whenGetRequestUrlThenReturnUrl();
+        HttpServletResponse servletResponseMock = mock(HttpServletResponse.class);
+        when(webRequestMock.getNativeResponse(HttpServletResponse.class)).thenReturn(servletResponseMock);
+        when(servletResponseMock.getWriter()).thenThrow(new IOException("Error"));
+
+        InputService service = new InputService();
+        service.setVersion("1.0");
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        Exception exception = assertThrows(HeaderException.class, () -> {
+            delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+        });
+
+        assertThat(exception.getMessage())
+            .isEqualTo("Unable to set header Location in put response for service " + SERVICE_NAME + ". Cause: Error");
+
+        ResponseEntity<List<Service>> response = delegateUnderTest.getServices();
+        assertThat(response.getBody()).isEmpty();
+    }
+
+    @Test
+    void getServices_shouldProvideArrayOfAddedServiceNames() throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        InputService service1 = new InputService();
+        service1.setDescription("description 1");
+        service1.setVersion(SERVICE_VERSION);
+        service1.setDisplayName("Display Name 1");
+
+        InputService service2 = new InputService();
+        service2.setDescription("description 2");
+        service2.setVersion(SERVICE_VERSION);
+        service2.setDisplayName("Display Name 2");
+
+        whenPrintResponseShouldWork();
+
+        String serviceName1 = "Service Name 1";
+        delegateUnderTest.putIndividualService(serviceName1, service1);
+        String serviceName2 = "Service Name 2";
+        delegateUnderTest.putIndividualService(serviceName2, service2);
+
+        ResponseEntity<List<Service>> response = delegateUnderTest.getServices();
+
+        assertThat(response.getStatusCode()).isEqualTo(OK);
+        List<Service> services = response.getBody();
+        assertThat(services).hasSize(2);
+        List<String> expectedServiceNames = Arrays.asList(serviceName1, serviceName2);
+        assertThat(expectedServiceNames).contains(services.get(0).getName()) //
+            .contains(services.get(1).getName());
+    }
+
+    @Test
+    void deleteService_shouldBeOk() throws Exception {
+        ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(webRequestMock);
+
+        InputService service = new InputService();
+        service.setDescription(SERVICE_DESCRIPTION);
+        service.setVersion(SERVICE_VERSION);
+        service.setDisplayName(SERVICE_DISPLAY_NAME);
+
+        whenPrintResponseShouldWork();
+
+        delegateUnderTest.putIndividualService(SERVICE_NAME, service);
+
+        ResponseEntity<List<Service>> servicesResponse = delegateUnderTest.getServices();
+
+        assertThat(servicesResponse.getBody()).hasSize(1);
+
+        ResponseEntity<Void> deleteResponse = delegateUnderTest.deleteIndividualService(SERVICE_NAME);
+
+        assertThat(deleteResponse.getStatusCode()).isEqualTo(NO_CONTENT);
+
+        servicesResponse = delegateUnderTest.getServices();
+
+        assertThat(servicesResponse.getBody()).isEmpty();
+    }
+
+    private void whenGetRequestUrlThenReturnUrl() {
+        whenGetRequestUrlThenReturnUrl("URL");
+    }
+
+    private void whenGetRequestUrlThenReturnUrl(String url) {
+        HttpServletRequest servletRequestMock = mock(HttpServletRequest.class);
+        when(webRequestMock.getNativeRequest(HttpServletRequest.class)).thenReturn(servletRequestMock);
+        when(servletRequestMock.getRequestURL()).thenReturn(new StringBuffer(url));
+    }
+
+    private HttpServletResponse whenPrintResponseShouldWork() {
+        return whenPrintResponseShouldWork("URL");
+    }
+
+    private HttpServletResponse whenPrintResponseShouldWork(String url) {
+        whenGetRequestUrlThenReturnUrl(url);
+        HttpServletResponse servletResponseMock = mock(HttpServletResponse.class);
+        when(webRequestMock.getNativeResponse(HttpServletResponse.class)).thenReturn(servletResponseMock);
+        PrintWriter printWriterMock = mock(PrintWriter.class);
+        try {
+            when(servletResponseMock.getWriter()).thenReturn(printWriterMock);
+        } catch (IOException e) {
+            // Nothing
+        }
+        return servletResponseMock;
+    }
+
+    private String getTodaysDate() {
+        long millis = System.currentTimeMillis();
+        Date date = new Date(millis);
+        return date.toString();
+    }
+}