I need to use APR protocol connector to replace the default NIO protocol connector in embedded Tomcat 9 of Springboot 2.3.12, may I ask how to implement it?
Asked
Active
Viewed 386 times
2
-
Isn't there any example in the docs? – HoRn Aug 22 '22 at 03:22
2 Answers
0
@Component
public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setProtocol("org.apache.coyote.http11.Http11AprProtocol");
}
}

DingHao
- 313
- 1
- 5
0
The solution bases on: https://dirask.com/posts/Spring-Boot-2-example-APR-native-library-configuration-with-Tomcat-9-Http11AprProtocol-pq6xxj
At first, you should install APR native libraries:
apt-get install libapr1-dev libssl-dev
Later, add to your project TomcatAprConfig.java
file:
package com.example.config;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.coyote.http11.Http11AprProtocol;
import org.apache.coyote.http2.Http2Protocol;
import org.apache.tomcat.util.buf.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.Ssl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Configuration
public class TomcatAprConfig {
// properties
private String publicCertificatePath = "/path/to/certificates/public_certificate.pem";
private String privateKeyPath = "/path/to/certificates/private_key.pem";
private int compressionMinSize = 2048;
private String compressionMimeTypes = "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml";
// public methods
@Bean(name = "tomcatServletWebServerFactory")
public TomcatServletWebServerFactory createServerFactory(ServerProperties serverProperties, ResourceLoader resourceLoader) {
TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory() {
@Override
public Ssl getSsl() {
return null; // null returned to stop the default SSL customizer
}
};
serverFactory.setProtocol("org.apache.coyote.http11.Http11AprProtocol"); // the protocol that will enable APR
serverFactory.setContextLifecycleListeners(this.createLifecycleListeners());
serverFactory.setTomcatConnectorCustomizers(this.createConnectorCustomizers(serverProperties, resourceLoader));
return serverFactory;
}
// private methods
private List<AprLifecycleListener> createLifecycleListeners() {
AprLifecycleListener lifecycleListener = new AprLifecycleListener();
return Collections.singletonList(lifecycleListener);
}
private List<TomcatConnectorCustomizer> createConnectorCustomizers(ServerProperties serverProperties, ResourceLoader resourceLoader) {
TomcatConnectorCustomizer connectorCustomizer = tomcatConnector -> {
Http11AprProtocol aprProtocol = (Http11AprProtocol) tomcatConnector.getProtocolHandler();
if (this.publicCertificatePath == null) {
throw new RuntimeException("Public certificate path is not configured.");
}
if (this.privateKeyPath == null) {
throw new RuntimeException("Private key path is not configured.");
}
Ssl connectionSsl = serverProperties.getSsl();
String[] connectionCiphers = connectionSsl.getCiphers();
String[] connectionProtocols = connectionSsl.getEnabledProtocols();
tomcatConnector.setSecure(true);
tomcatConnector.setScheme("https");
aprProtocol.setSSLEnabled(true);
if (connectionProtocols != null && connectionProtocols.length > 0) {
aprProtocol.setSslEnabledProtocols(this.joinStrings(connectionProtocols));
}
if (connectionCiphers != null && connectionCiphers.length > 0) {
aprProtocol.setCiphers(this.joinStrings(connectionCiphers));
}
try {
aprProtocol.setSSLCertificateFile(this.resolvePath(resourceLoader, this.publicCertificatePath));
aprProtocol.setSSLCertificateKeyFile(this.resolvePath(resourceLoader, this.privateKeyPath));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
Http2Protocol http2Protocol = new Http2Protocol();
http2Protocol.setCompression("on");
http2Protocol.setCompressibleMimeType(this.compressionMimeTypes);
http2Protocol.setCompressionMinSize(this.compressionMinSize);
tomcatConnector.addUpgradeProtocol(http2Protocol);
};
return Collections.singletonList(connectorCustomizer);
}
private String joinStrings(String[] strings) {
List<String> list = Arrays.asList(strings);
return StringUtils.join(list, ',');
}
private String resolvePath(ResourceLoader loader, String path) throws IOException {
Resource resource = loader.getResource(path);
try {
File file = resource.getFile();
return file.getAbsolutePath();
} catch (Exception ex) {
throw new IOException("Absolute '" + path + "' path resolving error.", ex);
}
}
}
Finally, run application using:
java -Djava.library.path="/usr/lib/x86_64-linux-gnu" -jar my-application.jar

Undqurek
- 11
- 1
- 1
- 3