Deregister JDBC drivers when deployed war's ServletContext is destroyed

Closes gh-21221
This commit is contained in:
Andy Wilkinson 2020-05-13 17:46:37 +01:00
parent 9e569cf1b0
commit 8b6cdbb977
2 changed files with 68 additions and 0 deletions

View File

@ -16,6 +16,9 @@
package org.springframework.boot.web.servlet.support;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import javax.servlet.Filter;
@ -98,7 +101,18 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit
// no-op because the application context is already initialized
}
@Override
public void contextDestroyed(ServletContextEvent event) {
try {
super.contextDestroyed(event);
}
finally {
deregisterJdbcDrivers(event.getServletContext());
}
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
@ -106,6 +120,28 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit
}
}
/**
* Deregisters the JDBC drivers that were registered by the application represented by
* the given {@code servletContext}. The default implementation
* {@link DriverManager#deregisterDriver(Driver) deregisters} every {@link Driver}
* that was loaded by the {@link ServletContext#getClassLoader web application's class
* loader}.
* @param servletContext the web application's servlet context
* @since 2.3.0
*/
protected void deregisterJdbcDrivers(ServletContext servletContext) {
for (Driver driver : Collections.list(DriverManager.getDrivers())) {
if (driver.getClass().getClassLoader() == servletContext.getClassLoader()) {
try {
DriverManager.deregisterDriver(driver);
}
catch (SQLException ex) {
// Continue
}
}
}
}
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());

View File

@ -17,12 +17,18 @@
package org.springframework.boot.web.servlet.support;
import java.util.Collections;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -46,6 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link SpringBootServletInitializer}.
@ -142,6 +149,31 @@ class SpringBootServletInitializerTests {
}
}
@Test
void whenServletContextIsDestroyedThenJdbcDriversAreDeregistered() throws ServletException {
ServletContext servletContext = mock(ServletContext.class);
given(servletContext.getInitParameterNames()).willReturn(new Vector<String>().elements());
given(servletContext.getAttributeNames()).willReturn(new Vector<String>().elements());
AtomicBoolean driversDeregistered = new AtomicBoolean();
new SpringBootServletInitializer() {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Config.class);
}
@Override
protected void deregisterJdbcDrivers(ServletContext servletContext) {
driversDeregistered.set(true);
}
}.onStartup(servletContext);
ArgumentCaptor<ServletContextListener> captor = ArgumentCaptor.forClass(ServletContextListener.class);
verify(servletContext).addListener(captor.capture());
captor.getValue().contextDestroyed(new ServletContextEvent(servletContext));
assertThat(driversDeregistered).isTrue();
}
static class PropertySourceVerifyingSpringBootServletInitializer extends SpringBootServletInitializer {
@Override