diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/CannotGetJdbcConnectionExceptionHandler.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/CannotGetJdbcConnectionExceptionHandler.java new file mode 100644 index 000000000..c2c370b39 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/CannotGetJdbcConnectionExceptionHandler.java @@ -0,0 +1,39 @@ +package com.iqser.red.service.peristence.v1.server.errorhandling; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.jdbc.CannotGetJdbcConnectionException; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class CannotGetJdbcConnectionExceptionHandler extends ISpringContextUncaughtExceptionService { + + public CannotGetJdbcConnectionExceptionHandler(ConfigurableApplicationContext context) { + + super(context); + } + + + @Override + public Class getExceptionClass() { + + return CannotGetJdbcConnectionException.class; + } + + + @Override + public void handleException(CannotGetJdbcConnectionException e) { + + log.error("Cannot get JDBC connection. Please check the database connection. Will terminate application now!: {}", e.getMessage(), e); + try { + SpringApplication.exit(context, () -> 0); + } catch (Exception ex) { + log.error("Error while trying to exit the application. Calling System.exit", ex); + System.exit(0); + } + } + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/ISpringContextUncaughtExceptionService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/ISpringContextUncaughtExceptionService.java new file mode 100644 index 000000000..1e6358f3b --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/ISpringContextUncaughtExceptionService.java @@ -0,0 +1,45 @@ +package com.iqser.red.service.peristence.v1.server.errorhandling; + +import org.springframework.context.ApplicationContext; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class ISpringContextUncaughtExceptionService { + + protected final ApplicationContext context; + + + protected ISpringContextUncaughtExceptionService(ApplicationContext context) {this.context = context;} + + + public final void handleUncaughtException(Thread t, Throwable e) { + + log.warn("Uncaught exception in thread: {}", t.getName(), e); + checkException(e); + + } + + + public abstract Class getExceptionClass(); + + + public abstract void handleException(T e); + + + private void checkException(Throwable e) { + + if (e == null) { + return; + } + var cause = e; + do { + if (cause.getClass().equals(getExceptionClass())) { + log.error("Handling exception: {}", cause.getMessage(), cause); + handleException((T) cause); + } + cause = cause.getCause(); + } while (cause != null); + } + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/SpringContextUncaughtExceptionHandler.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/SpringContextUncaughtExceptionHandler.java new file mode 100644 index 000000000..bb753fc1e --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/errorhandling/SpringContextUncaughtExceptionHandler.java @@ -0,0 +1,34 @@ +package com.iqser.red.service.peristence.v1.server.errorhandling; + +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class SpringContextUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { + + private final List iSpringContextUncaughtExceptionServices; + + + @PostConstruct + public void postConstruct() { + + Thread.setDefaultUncaughtExceptionHandler(this); + } + + + @Override + public void uncaughtException(Thread t, Throwable e) { + + iSpringContextUncaughtExceptionServices.forEach(service -> service.handleUncaughtException(t, e)); + + } + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/errorhandling/SpringContextUncaughtExceptionHandlerTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/errorhandling/SpringContextUncaughtExceptionHandlerTest.java new file mode 100644 index 000000000..615b2c78e --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/errorhandling/SpringContextUncaughtExceptionHandlerTest.java @@ -0,0 +1,100 @@ +package com.iqser.red.service.peristence.v1.server.integration.errorhandling; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.CannotGetJdbcConnectionException; +import org.springframework.test.context.junit4.SpringRunner; + +import com.iqser.red.service.peristence.v1.server.errorhandling.CannotGetJdbcConnectionExceptionHandler; +import com.iqser.red.service.peristence.v1.server.errorhandling.SpringContextUncaughtExceptionHandler; +import com.iqser.red.storage.commons.StorageAutoConfiguration; +import lombok.SneakyThrows; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = SpringContextUncaughtExceptionHandlerTest.SimpleApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class SpringContextUncaughtExceptionHandlerTest { + + @Autowired + public CannotGetJdbcConnectionExceptionHandlerTest cannotGetJdbcConnectionExceptionHandler; + + @Autowired + public SpringContextUncaughtExceptionHandler springContextUncaughtExceptionHandler; + + public static class CannotGetJdbcConnectionExceptionHandlerTest extends CannotGetJdbcConnectionExceptionHandler { + + private boolean hasException; + + + public CannotGetJdbcConnectionExceptionHandlerTest(ConfigurableApplicationContext context) { + + super(context); + } + + + @Override + public void handleException(CannotGetJdbcConnectionException e) { + + this.hasException = true; + } + + } + + + @Test + @SneakyThrows + public void testExceptionHandling() { + + assertThat(cannotGetJdbcConnectionExceptionHandler.hasException).isFalse(); + new Thread(() -> { + throw new RuntimeException("base exception", new CannotGetJdbcConnectionException("Test")); + }).start(); + Thread.sleep(500); + assertThat(cannotGetJdbcConnectionExceptionHandler.hasException).isTrue(); + } + + + @Configuration + public static class TestConfiguration { + + @Bean + public CannotGetJdbcConnectionExceptionHandlerTest cannotGetJdbcConnectionExceptionHandlerTest(ConfigurableApplicationContext context) { + + return new CannotGetJdbcConnectionExceptionHandlerTest(context); + } + + + @Bean + @Primary + public SpringContextUncaughtExceptionHandler springContextUncaughtExceptionHandler(CannotGetJdbcConnectionExceptionHandlerTest handler) { + + return new SpringContextUncaughtExceptionHandler(List.of(handler)); + } + + } + + @Import(TestConfiguration.class) + @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, StorageAutoConfiguration.class}) + public static class SimpleApplication { + + public static void main(String args[]) { + + SpringApplication.run(SimpleApplication.class, args); + } + + } + +}