Skip to content

Commit 8b6cdbb

Browse files
committed
Deregister JDBC drivers when deployed war's ServletContext is destroyed
Closes gh-21221
1 parent 9e569cf commit 8b6cdbb

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializer.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.web.servlet.support;
1818

19+
import java.sql.Driver;
20+
import java.sql.DriverManager;
21+
import java.sql.SQLException;
1922
import java.util.Collections;
2023

2124
import javax.servlet.Filter;
@@ -98,14 +101,47 @@ public void contextInitialized(ServletContextEvent event) {
98101
// no-op because the application context is already initialized
99102
}
100103

104+
@Override
105+
public void contextDestroyed(ServletContextEvent event) {
106+
try {
107+
super.contextDestroyed(event);
108+
}
109+
finally {
110+
deregisterJdbcDrivers(event.getServletContext());
111+
}
112+
}
113+
101114
});
115+
102116
}
103117
else {
104118
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
105119
+ "return an application context");
106120
}
107121
}
108122

123+
/**
124+
* Deregisters the JDBC drivers that were registered by the application represented by
125+
* the given {@code servletContext}. The default implementation
126+
* {@link DriverManager#deregisterDriver(Driver) deregisters} every {@link Driver}
127+
* that was loaded by the {@link ServletContext#getClassLoader web application's class
128+
* loader}.
129+
* @param servletContext the web application's servlet context
130+
* @since 2.3.0
131+
*/
132+
protected void deregisterJdbcDrivers(ServletContext servletContext) {
133+
for (Driver driver : Collections.list(DriverManager.getDrivers())) {
134+
if (driver.getClass().getClassLoader() == servletContext.getClassLoader()) {
135+
try {
136+
DriverManager.deregisterDriver(driver);
137+
}
138+
catch (SQLException ex) {
139+
// Continue
140+
}
141+
}
142+
}
143+
}
144+
109145
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
110146
SpringApplicationBuilder builder = createSpringApplicationBuilder();
111147
builder.main(getClass());

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@
1717
package org.springframework.boot.web.servlet.support;
1818

1919
import java.util.Collections;
20+
import java.util.Vector;
21+
import java.util.concurrent.atomic.AtomicBoolean;
2022

2123
import javax.servlet.ServletContext;
24+
import javax.servlet.ServletContextEvent;
25+
import javax.servlet.ServletContextListener;
26+
import javax.servlet.ServletException;
2227

2328
import org.junit.jupiter.api.AfterEach;
2429
import org.junit.jupiter.api.Test;
2530
import org.junit.jupiter.api.extension.ExtendWith;
31+
import org.mockito.ArgumentCaptor;
2632

2733
import org.springframework.boot.SpringApplication;
2834
import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -46,6 +52,7 @@
4652
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
4753
import static org.mockito.BDDMockito.given;
4854
import static org.mockito.Mockito.mock;
55+
import static org.mockito.Mockito.verify;
4956

5057
/**
5158
* Tests for {@link SpringBootServletInitializer}.
@@ -142,6 +149,31 @@ void servletContextPropertySourceIsAvailablePriorToRefresh() {
142149
}
143150
}
144151

152+
@Test
153+
void whenServletContextIsDestroyedThenJdbcDriversAreDeregistered() throws ServletException {
154+
ServletContext servletContext = mock(ServletContext.class);
155+
given(servletContext.getInitParameterNames()).willReturn(new Vector<String>().elements());
156+
given(servletContext.getAttributeNames()).willReturn(new Vector<String>().elements());
157+
AtomicBoolean driversDeregistered = new AtomicBoolean();
158+
new SpringBootServletInitializer() {
159+
160+
@Override
161+
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
162+
return builder.sources(Config.class);
163+
}
164+
165+
@Override
166+
protected void deregisterJdbcDrivers(ServletContext servletContext) {
167+
driversDeregistered.set(true);
168+
}
169+
170+
}.onStartup(servletContext);
171+
ArgumentCaptor<ServletContextListener> captor = ArgumentCaptor.forClass(ServletContextListener.class);
172+
verify(servletContext).addListener(captor.capture());
173+
captor.getValue().contextDestroyed(new ServletContextEvent(servletContext));
174+
assertThat(driversDeregistered).isTrue();
175+
}
176+
145177
static class PropertySourceVerifyingSpringBootServletInitializer extends SpringBootServletInitializer {
146178

147179
@Override

0 commit comments

Comments
 (0)