2021-03-16 14:25:15 +01:00
package eu.openaire.urls_controller ;
2023-10-09 16:23:59 +02:00
import com.zaxxer.hikari.HikariDataSource ;
2023-07-24 18:55:37 +02:00
import eu.openaire.urls_controller.controllers.BulkImportController ;
2023-05-29 11:21:48 +02:00
import eu.openaire.urls_controller.controllers.UrlsController ;
2023-02-24 22:49:04 +01:00
import eu.openaire.urls_controller.services.UrlsServiceImpl ;
2022-12-15 17:34:28 +01:00
import eu.openaire.urls_controller.util.FileUtils ;
2022-02-22 12:54:16 +01:00
import eu.openaire.urls_controller.util.UriBuilder ;
2023-05-24 12:52:28 +02:00
import io.micrometer.core.aop.TimedAspect ;
import io.micrometer.core.instrument.MeterRegistry ;
2022-02-04 13:48:22 +01:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2023-10-09 16:23:59 +02:00
import org.springframework.beans.factory.annotation.Autowired ;
2022-02-22 12:54:16 +01:00
import org.springframework.boot.CommandLineRunner ;
2021-03-16 14:25:15 +01:00
import org.springframework.boot.SpringApplication ;
import org.springframework.boot.autoconfigure.SpringBootApplication ;
2022-09-12 16:04:05 +02:00
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext ;
2023-05-24 12:42:29 +02:00
import org.springframework.context.ConfigurableApplicationContext ;
2021-03-16 14:25:15 +01:00
import org.springframework.context.annotation.Bean ;
import org.springframework.core.env.Environment ;
2021-12-21 14:55:27 +01:00
import org.springframework.scheduling.annotation.EnableScheduling ;
2021-03-16 14:25:15 +01:00
import org.springframework.web.cors.CorsConfiguration ;
import org.springframework.web.cors.CorsConfigurationSource ;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource ;
2022-02-04 13:48:22 +01:00
import javax.annotation.PreDestroy ;
2021-03-16 14:25:15 +01:00
import java.util.Arrays ;
import java.util.Collections ;
2022-12-15 17:34:28 +01:00
import java.util.concurrent.ExecutorService ;
2022-02-04 13:48:22 +01:00
import java.util.concurrent.TimeUnit ;
2021-03-16 14:25:15 +01:00
@SpringBootApplication
@EnableScheduling
public class Application {
2022-02-04 13:48:22 +01:00
private static final Logger logger = LoggerFactory . getLogger ( Application . class ) ;
2023-10-09 16:23:59 +02:00
@Autowired
HikariDataSource hikariDataSource ;
2023-05-24 12:42:29 +02:00
private static ConfigurableApplicationContext context ;
2021-03-16 14:25:15 +01:00
public static void main ( String [ ] args ) {
2023-05-24 12:42:29 +02:00
context = SpringApplication . run ( Application . class , args ) ;
2021-03-16 14:25:15 +01:00
}
@Bean
public CorsConfigurationSource corsConfigurationSource ( ) {
CorsConfiguration configuration = new CorsConfiguration ( ) ;
configuration . setAllowedOrigins ( Collections . singletonList ( " * " ) ) ;
configuration . setAllowedMethods ( Arrays . asList ( " GET " , " POST " , " PUT " , " PATCH " , " DELETE " , " OPTIONS " ) ) ;
configuration . setAllowedHeaders ( Arrays . asList ( " authorization " , " content-type " , " x-auth-token " ) ) ;
configuration . setExposedHeaders ( Collections . singletonList ( " x-auth-token " ) ) ;
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ( ) ;
source . registerCorsConfiguration ( " /** " , configuration ) ;
return source ;
}
2022-02-04 13:48:22 +01:00
2023-10-09 16:23:59 +02:00
public void gentleAppShutdown ( )
2023-05-24 12:42:29 +02:00
{
2023-10-09 16:23:59 +02:00
shutdownThreads ( ) ;
2023-05-24 12:42:29 +02:00
int exitCode = 0 ;
try {
exitCode = SpringApplication . exit ( context , ( ) - > 0 ) ; // The "PreDestroy" method will be called. (the "context" will be closed automatically (I checked it))
} catch ( IllegalArgumentException iae ) {
logger . error ( iae . getMessage ( ) ) ; // This will say "Context must not be null", in case the "gentleAppShutdown()" was called too early in the app's lifetime. But it's ok.
}
System . exit ( exitCode ) ;
}
2023-10-09 16:23:59 +02:00
private boolean haveThreadsShutdown = false ;
2022-02-04 13:48:22 +01:00
@PreDestroy
2023-10-09 16:23:59 +02:00
public void shutdownThreads ( )
{
// Normally this methods will have already been executed by "gentleAppShutdown()" just before the "PreDestroy" has called this method again.
// BUT, in case the service was shutdown from the OS, without the use of the "ShutdownService-API, then this will be the 1st time this method is called.
if ( haveThreadsShutdown )
return ;
2022-02-04 13:48:22 +01:00
logger . info ( " Shutting down the threads.. " ) ;
2022-12-15 17:34:28 +01:00
2023-10-09 16:23:59 +02:00
shutdownThreadsForExecutorService ( UrlsServiceImpl . insertsExecutor ) ;
shutdownThreadsForExecutorService ( FileUtils . hashMatchingExecutor ) ;
shutdownThreadsForExecutorService ( BulkImportController . bulkImportExecutor ) ;
shutdownThreadsForExecutorService ( UrlsController . backgroundExecutor ) ;
haveThreadsShutdown = true ;
// For some reason the Hikari Datasource cannot close properly by Spring Boot, unless we explicitly call close here.
hikariDataSource . close ( ) ;
2022-12-15 17:34:28 +01:00
logger . info ( " Exiting.. " ) ;
}
2023-10-09 16:23:59 +02:00
private void shutdownThreadsForExecutorService ( ExecutorService executorService ) throws RuntimeException
2022-12-15 17:34:28 +01:00
{
executorService . shutdown ( ) ; // Define that no new tasks will be scheduled.
2022-02-04 13:48:22 +01:00
try {
2023-05-23 20:09:34 +02:00
if ( ! executorService . awaitTermination ( 2 , TimeUnit . MINUTES ) ) {
2022-02-04 13:48:22 +01:00
logger . warn ( " The working threads did not finish on time! Stopping them immediately.. " ) ;
2022-12-15 17:34:28 +01:00
executorService . shutdownNow ( ) ;
2023-10-09 16:23:59 +02:00
// Wait a while for tasks to respond to being cancelled (thus terminated).
if ( ! executorService . awaitTermination ( 1 , TimeUnit . MINUTES ) )
logger . warn ( " The executor " + executorService + " could not be terminated! " ) ;
2022-02-04 13:48:22 +01:00
}
} catch ( SecurityException se ) {
logger . error ( " Could not shutdown the threads in any way..! " , se ) ;
} catch ( InterruptedException ie ) {
try {
2022-12-15 17:34:28 +01:00
executorService . shutdownNow ( ) ;
2023-10-09 16:23:59 +02:00
// Wait a while for tasks to respond to being cancelled (thus terminated).
if ( ! executorService . awaitTermination ( 1 , TimeUnit . MINUTES ) )
logger . warn ( " The executor " + executorService + " could not be terminated! " ) ;
2022-02-04 13:48:22 +01:00
} catch ( SecurityException se ) {
logger . error ( " Could not shutdown the threads in any way..! " , se ) ;
2023-10-09 16:23:59 +02:00
} catch ( InterruptedException e ) {
throw new RuntimeException ( e ) ;
2022-02-04 13:48:22 +01:00
}
}
}
2022-02-22 12:54:16 +01:00
@Bean
2022-09-12 16:04:05 +02:00
public CommandLineRunner setServerBaseUrl ( Environment environment , ServletWebServerApplicationContext webServerAppCtxt )
{
return args - > new UriBuilder ( environment , webServerAppCtxt ) ;
2022-02-22 12:54:16 +01:00
}
2021-03-16 14:25:15 +01:00
2023-05-24 12:52:28 +02:00
@Bean
public TimedAspect timedAspect ( MeterRegistry registry ) {
return new TimedAspect ( registry ) ;
}
2021-03-16 14:25:15 +01:00
}