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
This commit is contained in:
Brian Clozel 2017-10-31 22:46:08 +01:00
parent 5cf50b6ccc
commit 5f57578b71
4 changed files with 79 additions and 1 deletions

View File

@ -24,7 +24,9 @@ import java.util.List;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.AbstractProtocol;
@ -53,6 +55,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
private String protocol = DEFAULT_PROTOCOL;
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener());
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
private List<TomcatConnectorCustomizer> tomcatConnectorCustomizers = new ArrayList<>();
@ -115,6 +119,9 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
* @param context the Tomcat context
*/
protected void configureContext(Context context) {
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
@ -211,6 +218,39 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
return this.tomcatConnectorCustomizers;
}
/**
* Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context}
* . Calling this method will replace any existing listeners.
* @param contextLifecycleListeners the listeners to set
*/
public void setContextLifecycleListeners(
Collection<? extends LifecycleListener> contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners,
"ContextLifecycleListeners must not be null");
this.contextLifecycleListeners = new ArrayList<>(contextLifecycleListeners);
}
/**
* Returns a mutable collection of the {@link LifecycleListener}s that will be applied
* to the Tomcat {@link Context} .
* @return the context lifecycle listeners that will be applied
*/
public Collection<LifecycleListener> getContextLifecycleListeners() {
return this.contextLifecycleListeners;
}
/**
* Add {@link LifecycleListener}s that should be added to the Tomcat {@link Context}.
* @param contextLifecycleListeners the listeners to add
*/
public void addContextLifecycleListeners(
LifecycleListener... contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners,
"ContextLifecycleListeners must not be null");
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
}
/**
* Factory method called to create the {@link TomcatWebServer}. Subclasses can
* override this method to return a different {@link TomcatWebServer} or apply

View File

@ -50,6 +50,7 @@ import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.apache.catalina.WebResourceSet;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.Tomcat;
@ -122,7 +123,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
private List<Valve> contextValves = new ArrayList<>();
private List<LifecycleListener> contextLifecycleListeners = new ArrayList<>();
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener());
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();

View File

@ -19,13 +19,17 @@ package org.springframework.boot.web.embedded.tomcat;
import java.util.Arrays;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.junit.Test;
import org.mockito.InOrder;
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
import org.springframework.http.server.reactive.HttpHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@ -60,6 +64,30 @@ public class TomcatReactiveWebServerFactoryTests
}
}
@Test
public void defaultTomcatListeners() throws Exception {
TomcatReactiveWebServerFactory factory = getFactory();
assertThat(factory.getContextLifecycleListeners())
.hasSize(1)
.first().isInstanceOf(AprLifecycleListener.class);
}
@Test
public void tomcatListeners() throws Exception {
TomcatReactiveWebServerFactory factory = getFactory();
LifecycleListener[] listeners = new LifecycleListener[4];
for (int i = 0; i < listeners.length; i++) {
listeners[i] = mock(LifecycleListener.class);
}
factory.setContextLifecycleListeners(Arrays.asList(listeners[0], listeners[1]));
factory.addContextLifecycleListeners(listeners[2], listeners[3]);
this.webServer = factory.getWebServer(mock(HttpHandler.class));
InOrder ordered = inOrder((Object[]) listeners);
for (LifecycleListener listener : listeners) {
ordered.verify(listener).lifecycleEvent(any(LifecycleEvent.class));
}
}
@Test
public void setNullConnectorCustomizersShouldThrowException() {
TomcatReactiveWebServerFactory factory = getFactory();

View File

@ -37,6 +37,7 @@ import org.apache.catalina.Service;
import org.apache.catalina.SessionIdGenerator;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.util.CharsetMapper;
@ -102,6 +103,14 @@ public class TomcatServletWebServerFactoryTests
tomcatWebServer.stop();
}
@Test
public void defaultTomcatListeners() throws Exception {
TomcatServletWebServerFactory factory = getFactory();
assertThat(factory.getContextLifecycleListeners())
.hasSize(1)
.first().isInstanceOf(AprLifecycleListener.class);
}
@Test
public void tomcatListeners() throws Exception {
TomcatServletWebServerFactory factory = getFactory();