Skip to content

Commit 5f57578

Browse files
committed
Enable APR optional support in Tomcat servers
This commits adds the AprLifecycleListener as a default LifecycleListener in order to detect and use automatically the tomcatnative library if it is available, for SSL support. This feature can be useful for both performance reasons or for supporting ALPN when using JDK8. See gh-10043 Closes gh-9964
1 parent 5cf50b6 commit 5f57578

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525
import org.apache.catalina.Context;
2626
import org.apache.catalina.Host;
27+
import org.apache.catalina.LifecycleListener;
2728
import org.apache.catalina.connector.Connector;
29+
import org.apache.catalina.core.AprLifecycleListener;
2830
import org.apache.catalina.loader.WebappLoader;
2931
import org.apache.catalina.startup.Tomcat;
3032
import org.apache.coyote.AbstractProtocol;
@@ -53,6 +55,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
5355

5456
private String protocol = DEFAULT_PROTOCOL;
5557

58+
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener());
59+
5660
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
5761

5862
private List<TomcatConnectorCustomizer> tomcatConnectorCustomizers = new ArrayList<>();
@@ -115,6 +119,9 @@ protected void prepareContext(Host host, TomcatHttpHandlerAdapter servlet) {
115119
* @param context the Tomcat context
116120
*/
117121
protected void configureContext(Context context) {
122+
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
123+
context.addLifecycleListener(lifecycleListener);
124+
}
118125
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
119126
customizer.customize(context);
120127
}
@@ -211,6 +218,39 @@ public Collection<TomcatConnectorCustomizer> getTomcatConnectorCustomizers() {
211218
return this.tomcatConnectorCustomizers;
212219
}
213220

221+
/**
222+
* Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context}
223+
* . Calling this method will replace any existing listeners.
224+
* @param contextLifecycleListeners the listeners to set
225+
*/
226+
public void setContextLifecycleListeners(
227+
Collection<? extends LifecycleListener> contextLifecycleListeners) {
228+
Assert.notNull(contextLifecycleListeners,
229+
"ContextLifecycleListeners must not be null");
230+
this.contextLifecycleListeners = new ArrayList<>(contextLifecycleListeners);
231+
}
232+
233+
/**
234+
* Returns a mutable collection of the {@link LifecycleListener}s that will be applied
235+
* to the Tomcat {@link Context} .
236+
* @return the context lifecycle listeners that will be applied
237+
*/
238+
public Collection<LifecycleListener> getContextLifecycleListeners() {
239+
return this.contextLifecycleListeners;
240+
}
241+
242+
/**
243+
* Add {@link LifecycleListener}s that should be added to the Tomcat {@link Context}.
244+
* @param contextLifecycleListeners the listeners to add
245+
*/
246+
public void addContextLifecycleListeners(
247+
LifecycleListener... contextLifecycleListeners) {
248+
Assert.notNull(contextLifecycleListeners,
249+
"ContextLifecycleListeners must not be null");
250+
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
251+
}
252+
253+
214254
/**
215255
* Factory method called to create the {@link TomcatWebServer}. Subclasses can
216256
* override this method to return a different {@link TomcatWebServer} or apply

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.apache.catalina.WebResourceSet;
5151
import org.apache.catalina.Wrapper;
5252
import org.apache.catalina.connector.Connector;
53+
import org.apache.catalina.core.AprLifecycleListener;
5354
import org.apache.catalina.loader.WebappLoader;
5455
import org.apache.catalina.session.StandardManager;
5556
import org.apache.catalina.startup.Tomcat;
@@ -122,7 +123,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
122123

123124
private List<Valve> contextValves = new ArrayList<>();
124125

125-
private List<LifecycleListener> contextLifecycleListeners = new ArrayList<>();
126+
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener());
126127

127128
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
128129

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
import java.util.Arrays;
2020

2121
import org.apache.catalina.Context;
22+
import org.apache.catalina.LifecycleEvent;
23+
import org.apache.catalina.LifecycleListener;
2224
import org.apache.catalina.connector.Connector;
25+
import org.apache.catalina.core.AprLifecycleListener;
2326
import org.junit.Test;
2427
import org.mockito.InOrder;
2528

2629
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
2730
import org.springframework.http.server.reactive.HttpHandler;
2831

32+
import static org.assertj.core.api.Assertions.assertThat;
2933
import static org.mockito.ArgumentMatchers.any;
3034
import static org.mockito.Mockito.inOrder;
3135
import static org.mockito.Mockito.mock;
@@ -60,6 +64,30 @@ public void tomcatCustomizers() throws Exception {
6064
}
6165
}
6266

67+
@Test
68+
public void defaultTomcatListeners() throws Exception {
69+
TomcatReactiveWebServerFactory factory = getFactory();
70+
assertThat(factory.getContextLifecycleListeners())
71+
.hasSize(1)
72+
.first().isInstanceOf(AprLifecycleListener.class);
73+
}
74+
75+
@Test
76+
public void tomcatListeners() throws Exception {
77+
TomcatReactiveWebServerFactory factory = getFactory();
78+
LifecycleListener[] listeners = new LifecycleListener[4];
79+
for (int i = 0; i < listeners.length; i++) {
80+
listeners[i] = mock(LifecycleListener.class);
81+
}
82+
factory.setContextLifecycleListeners(Arrays.asList(listeners[0], listeners[1]));
83+
factory.addContextLifecycleListeners(listeners[2], listeners[3]);
84+
this.webServer = factory.getWebServer(mock(HttpHandler.class));
85+
InOrder ordered = inOrder((Object[]) listeners);
86+
for (LifecycleListener listener : listeners) {
87+
ordered.verify(listener).lifecycleEvent(any(LifecycleEvent.class));
88+
}
89+
}
90+
6391
@Test
6492
public void setNullConnectorCustomizersShouldThrowException() {
6593
TomcatReactiveWebServerFactory factory = getFactory();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.catalina.SessionIdGenerator;
3838
import org.apache.catalina.Valve;
3939
import org.apache.catalina.connector.Connector;
40+
import org.apache.catalina.core.AprLifecycleListener;
4041
import org.apache.catalina.core.StandardWrapper;
4142
import org.apache.catalina.startup.Tomcat;
4243
import org.apache.catalina.util.CharsetMapper;
@@ -102,6 +103,14 @@ public void tomcatEngineNames() throws Exception {
102103
tomcatWebServer.stop();
103104
}
104105

106+
@Test
107+
public void defaultTomcatListeners() throws Exception {
108+
TomcatServletWebServerFactory factory = getFactory();
109+
assertThat(factory.getContextLifecycleListeners())
110+
.hasSize(1)
111+
.first().isInstanceOf(AprLifecycleListener.class);
112+
}
113+
105114
@Test
106115
public void tomcatListeners() throws Exception {
107116
TomcatServletWebServerFactory factory = getFactory();

0 commit comments

Comments
 (0)