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;
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();
@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
@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;
}
}
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;
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));
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;
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;
@Autowired
private EiTypes eiTypes;
+ @Autowired
+ private EiProducers eiProducers;
+
@Autowired
ProducerCallbacks producerCallbacks;
}
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) {
@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);
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);
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;
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;
/**
* Callbacks to the EiProducer
*/
-@Component
@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
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()), //
* @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()))); //
}
* @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();
.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);
});
}
- 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());
}
}
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;
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;
@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(
@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)
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));
}
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();
}
+
}
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;
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 {
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() {
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());
} catch (IOException e) {
logger.warn("Could not remove file: {}", e.getMessage());
}
-
+ this.producerCallbacks.stopEiJob(job, eiProducers);
}
public synchronized int size() {
this.allEiJobs.clear();
this.jobsByType.clear();
jobsByOwner.clear();
+ clearDatabase();
+ }
+
+ private void clearDatabase() {
try {
FileSystemUtils.deleteRecursively(Path.of(getDatabaseDirectory()));
Files.createDirectories(Paths.get(getDatabaseDirectory()));
}
}
- 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) {
}
private String getDatabaseDirectory() {
- return config.getVardataDirectory() + "/database";
+ return config.getVardataDirectory() + "/eijobs";
}
}
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() {
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;
}
}
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 {
@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());
- }
}
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.
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() {
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() {
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";
}
}
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) {
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);
}
}
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;
@Autowired
ProducerSupervision producerSupervision;
+ @Autowired
+ ProducerCallbacks producerCallbacks;
+
private static Gson gson = new GsonBuilder().create();
/**
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);
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");
// 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
{
// 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) {
String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
restClient().putForEntity(url, body).block();
+
return this.eiTypes.getType(eiTypeId);
}
--- /dev/null
+<?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>
<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
@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);
}
-/*-\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();
+ }
+}
package org.oransc.rappcatalogue.exception;
-public class HeaderException extends RuntimeException {
+public class HeaderException extends Exception {
private static final long serialVersionUID = -7798178963078284655L;
package org.oransc.rappcatalogue.exception;
-public class InvalidServiceException extends RuntimeException {
+public class InvalidServiceException extends Exception {
private static final long serialVersionUID = 3849219105170316564L;
public InvalidServiceException() {
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) {
-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();
+ }
+}