}},
"tags": ["Data consumer (callbacks)"]
}},
+ "/actuator/shutdown": {"post": {
+ "summary": "Actuator web endpoint 'shutdown'",
+ "operationId": "shutdown",
+ "responses": {"200": {
+ "description": "OK",
+ "content": {"*/*": {"schema": {"type": "object"}}}
+ }},
+ "tags": ["Actuator"]
+ }},
"/actuator/metrics/{requiredMetricName}": {"get": {
"summary": "Actuator web endpoint 'metrics-requiredMetricName'",
"operationId": "metrics-requiredMetricName",
application/json:
schema:
$ref: '#/components/schemas/Void'
+ /actuator/shutdown:
+ post:
+ tags:
+ - Actuator
+ summary: Actuator web endpoint 'shutdown'
+ operationId: shutdown
+ responses:
+ 200:
+ description: OK
+ content:
+ '*/*':
+ schema:
+ type: object
/actuator/metrics/{requiredMetricName}:
get:
tags:
web:
exposure:
# Enabling of springboot actuator features. See springboot documentation.
- include: "loggers,logfile,health,info,metrics,threaddump,heapdump"
-
+ include: "loggers,logfile,health,info,metrics,threaddump,heapdump,shutdown"
+ endpoint:
+ shutdown:
+ enabled: true
+lifecycle:
+ timeout-per-shutdown-phase: "20s"
logging:
# Configuration of logging
level:
key-store: /opt/app/information-coordinator-service/etc/cert/keystore.jks
key-password: policy_agent
key-alias: policy_agent
+ shutdown: "graceful"
app:
webclient:
# Configuration of the trust store used for the HTTP client (outgoing requests)
package org.oransc.ics;
+import java.lang.invoke.MethodHandles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
public static void main(String[] args) {
- SpringApplication.run(Application.class);
+ ConfigurableApplicationContext context = SpringApplication.run(Application.class);
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ logger.warn("Shutting down, received signal SIGTERM");
+ SpringApplication.exit(context);
+ }
+ });
}
-
}
import org.apache.catalina.connector.Connector;
import org.oransc.ics.clients.SecurityContext;
import org.oransc.ics.configuration.ApplicationConfig;
+import org.oransc.ics.controllers.a1e.A1eCallbacks;
import org.oransc.ics.controllers.r1producer.ProducerCallbacks;
import org.oransc.ics.repository.InfoJobs;
+import org.oransc.ics.repository.InfoProducers;
import org.oransc.ics.repository.InfoTypeSubscriptions;
import org.oransc.ics.repository.InfoTypes;
import org.slf4j.Logger;
return this.producerCallbacks;
}
+ @Bean
+ public InfoProducers getInfoProducers(ProducerCallbacks producerCallbacks, A1eCallbacks consumerCallbacks,
+ InfoJobs infoJobs, InfoTypes infoTypes) {
+ return new InfoProducers(producerCallbacks, consumerCallbacks, infoJobs, infoTypes);
+ }
+
@Bean
public ApplicationConfig getApplicationConfig() {
return this.applicationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
-import org.springframework.stereotype.Component;
/**
* Dynamic representation of all EiProducers.
*/
@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
-@Component
public class InfoProducers {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Map<String, InfoProducer> allInfoProducers = new HashMap<>();
import org.json.JSONObject;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
import org.oransc.ics.clients.AsyncRestClient;
import org.oransc.ics.clients.AsyncRestClientFactory;
import org.oransc.ics.clients.SecurityContext;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
+import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@TestMethodOrder(MethodOrderer.MethodName.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@TestPropertySource(
properties = { //
}
+ @Test
+ @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+ void testZZActuator() throws Exception {
+ // The test must be run last, hence the "ZZ" in the name. All succeeding tests
+ // will fail.
+ AsyncRestClient client = restClient();
+ client.post("/actuator/loggers/org.oransc.ics", "{\"configuredLevel\":\"trace\"}").block();
+ String resp = client.get("/actuator/loggers/org.oransc.ics").block();
+ assertThat(resp).contains("TRACE");
+ client.post("/actuator/loggers/org.springframework.boot.actuate", "{\"configuredLevel\":\"trace\"}").block();
+ // This will stop the web server and all coming tests will fail.
+ client.post("/actuator/shutdown", "").block();
+ Thread.sleep(1000);
+
+ StepVerifier.create(restClient().get(ConsumerConsts.API_ROOT + "/info-jobs")) // Any call
+ .expectSubscription() //
+ .expectErrorMatches(t -> t instanceof WebClientRequestException) //
+ .verify();
+ }
+
private String typeSubscriptionUrl() {
return ConsumerConsts.API_ROOT + "/info-type-subscription";
}