HOTFIX Error handling for jdbc connection error, pool out of connections

This commit is contained in:
Timo Bejan 2024-05-08 13:44:51 +03:00
parent 7eb8ca2013
commit 063576243a
4 changed files with 218 additions and 0 deletions

View File

@ -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<CannotGetJdbcConnectionException> {
public CannotGetJdbcConnectionExceptionHandler(ConfigurableApplicationContext context) {
super(context);
}
@Override
public Class<CannotGetJdbcConnectionException> 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);
}
}
}

View File

@ -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<T extends Throwable> {
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<T> 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);
}
}

View File

@ -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<ISpringContextUncaughtExceptionService> iSpringContextUncaughtExceptionServices;
@PostConstruct
public void postConstruct() {
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
iSpringContextUncaughtExceptionServices.forEach(service -> service.handleUncaughtException(t, e));
}
}

View File

@ -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);
}
}
}