others-how to solve java.lang.UnsatisfiedLinkError when adding http/2 support for springboot applications?

1. Purpose

In this post, I will show you how to solve java.lang.UnsatisfiedLinkError when adding http/2 support for springboot applications.

Here is the detail error stacktrace:

[root@local springboot2-mvc]# ./start.sh

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.2.RELEASE)

2023-08-31 14:18:18.359  INFO 30021 --- [           main] springboot2.mvc.Application              : Starting Application on local.server.harbor with PID 30021 (/root/http2/springboot2-mvc/springboot2-mvc-1.0-exec.jar started by root in /root/http2/springboot2-mvc)
2023-08-31 14:18:18.362  INFO 30021 --- [           main] springboot2.mvc.Application              : No active profile set, falling back to default profiles: default
2023-08-31 14:18:18.453  INFO 30021 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@22a71081: startup date [Thu Aug 31 14:18:18 CST 2023]; root of context hierarchy
2023-08-31 14:18:19.540  WARN 30021 --- [           main] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library failed to load. The error reported was [/usr/local/apr/lib/libtcnative-1.so.0.2.38: /usr/local/apr/lib/libtcnative-1.so.0.2.38: undefined symbol: BN_get_rfc3526_prime_8192]

java.lang.UnsatisfiedLinkError: /usr/local/apr/lib/libtcnative-1.so.0.2.38: /usr/local/apr/lib/libtcnative-1.so.0.2.38: undefined symbol: BN_get_rfc3526_prime_8192
    at java.lang.ClassLoader$NativeLibrary.load(Native Method) ~[na:1.8.0_231]
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1934) ~[na:1.8.0_231]
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1850) ~[na:1.8.0_231]
    at java.lang.Runtime.loadLibrary0(Runtime.java:870) ~[na:1.8.0_231]
    at java.lang.System.loadLibrary(System.java:1122) ~[na:1.8.0_231]
    at org.apache.tomcat.jni.Library.<init>(Library.java:42) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at org.apache.tomcat.jni.Library.initialize(Library.java:178) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at org.apache.catalina.core.AprLifecycleListener.init(AprLifecycleListener.java:198) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at org.apache.catalina.core.AprLifecycleListener.isAprAvailable(AprLifecycleListener.java:107) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at org.apache.catalina.connector.Connector.setProtocol(Connector.java:582) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at org.apache.catalina.connector.Connector.<init>(Connector.java:74) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
    at springboot2.mvc.config.TomcatConfig$1.customize(TomcatConfig.java:30) [classes!/:na]
    at springboot2.mvc.config.TomcatConfig$1.customize(TomcatConfig.java:24) [classes!/:na]
    at org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor.lambda$postProcessBeforeInitialization$0(WebServerFactoryCustomizerBeanPostProcessor.java:78) [spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.util.LambdaSafe$Callbacks.lambda$null$0(LambdaSafe.java:291) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.util.LambdaSafe$LambdaSafeCallback.invoke(LambdaSafe.java:162) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.util.LambdaSafe$Callbacks.lambda$invoke$1(LambdaSafe.java:290) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_231]
    at java.util.Collections$UnmodifiableCollection.forEach(Collections.java:1082) ~[na:1.8.0_231]
    at org.springframework.boot.util.LambdaSafe$Callbacks.invoke(LambdaSafe.java:289) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor.postProcessBeforeInitialization(WebServerFactoryCustomizerBeanPostProcessor.java:78) [spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor.postProcessBeforeInitialization(WebServerFactoryCustomizerBeanPostProcessor.java:61) [spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1698) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:214) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:178) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544) ~[spring-context-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) ~[spring-boot-2.0.2.RELEASE.jar!/:2.0.2.RELEASE]
    at springboot2.mvc.Application.main(Application.java:13) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_231]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_231]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_231]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_231]
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[springboot2-mvc-1.0-exec.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[springboot2-mvc-1.0-exec.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) ~[springboot2-mvc-1.0-exec.jar:na]
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) ~[springboot2-mvc-1.0-exec.jar:na]

2023-08-31 14:18:19.669  INFO 30021 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https) 8082 (http)

The core error is:

2023-08-31 14:18:19.540  WARN 30021 --- [           main] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library failed to load. The error reported was [/usr/local/apr/lib/libtcnative-1.so.0.2.38: /usr/local/apr/lib/libtcnative-1.so.0.2.38: undefined symbol: BN_get_rfc3526_prime_8192]

java.lang.UnsatisfiedLinkError: /usr/local/apr/lib/libtcnative-1.so.0.2.38: /usr/local/apr/lib/libtcnative-1.so.0.2.38: undefined symbol: BN_get_rfc3526_prime_8192

Here is the /usr/local/apr/lib:

[root@local springboot2-mvc]# ll /usr/local/apr/lib
Total 8360
-rw-r--r--. 1 root root   10110 8月  30 17:55 apr.exp
drwxr-xr-x. 2 root root     111 8月  30 17:56 apr-util-1
-rw-r--r--. 1 root root    5856 8月  30 17:56 aprutil.exp
-rw-r--r--. 1 root root 2058510 8月  30 17:55 libapr-1.a
-rwxr-xr-x. 1 root root     971 8月  30 17:55 libapr-1.la
lrwxrwxrwx. 1 root root      17 8月  30 17:55 libapr-1.so -> libapr-1.so.0.6.5
lrwxrwxrwx. 1 root root      17 8月  30 17:55 libapr-1.so.0 -> libapr-1.so.0.6.5
-rwxr-xr-x. 1 root root 1146040 8月  30 17:55 libapr-1.so.0.6.5
-rw-r--r--. 1 root root 1348480 8月  30 17:56 libaprutil-1.a
-rwxr-xr-x. 1 root root    1038 8月  30 17:56 libaprutil-1.la
lrwxrwxrwx. 1 root root      21 8月  30 17:56 libaprutil-1.so -> libaprutil-1.so.0.6.3
lrwxrwxrwx. 1 root root      21 8月  30 17:56 libaprutil-1.so.0 -> libaprutil-1.so.0.6.3
-rwxr-xr-x. 1 root root  811144 8月  30 17:56 libaprutil-1.so.0.6.3
-rw-r--r--. 1 root root 1972860 8月  30 18:02 libtcnative-1.a
-rwxr-xr-x. 1 root root    1054 8月  30 18:02 libtcnative-1.la
lrwxrwxrwx. 1 root root      23 8月  30 18:02 libtcnative-1.so -> libtcnative-1.so.0.2.38
lrwxrwxrwx. 1 root root      23 8月  30 18:02 libtcnative-1.so.0 -> libtcnative-1.so.0.2.38
-rwxr-xr-x. 1 root root 1177776 8月  30 18:02 libtcnative-1.so.0.2.38
drwxr-xr-x. 2 root root      43 8月  30 17:56 pkgconfig

The tomcat engine springboot using is:

Starting Servlet Engine: Apache Tomcat/8.5.31



2. Solution

2.1 What is libtcnative?

Tomcat can use the Apache Portable Runtime to provide superior scalability, performance, and better integration with native server technologies. The Apache Portable Runtime is a highly portable library that is at the heart of Apache HTTP Server 2.x. APR has many uses, including access to advanced IO functionality (such as sendfile, epoll and OpenSSL), OS level functionality (random number generation, system status, etc), and native process handling (shared memory, NT pipes and Unix sockets).

These features allows making Tomcat a general purpose webserver, will enable much better integration with other native web technologies, and overall make Java much more viable as a full fledged webserver platform rather than simply a backend focused technology.

2.2 Why need libtcnative?

Spring Boot ships by default with Tomcat 8.5.x. With that version, HTTP/2 is only supported if the libtcnative library and its dependencies are installed on the host operating system.

The library folder must be made available, if not already, to the JVM library path. You can do so with a JVM argument such as -Djava.library.path=/usr/local/opt/tomcat-native/lib. More on this in the official Tomcat documentation.

2.3 The solution

The tomcat I used to install libtcnative is apache-tomcat-8.5.93, is that the problem?

Try to install tomcat native again:

yum install tomcat-native

then query its install directory:

rpm -ql tomcat-native

then change the startup script:

java -cp . -Djava.library.path=/usr/lib64/  -jar *.jar

Then start springboot again, it works~

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.2.RELEASE)

2023-08-31 15:39:54.415  INFO 32466 --- [           main] springboot2.mvc.Application              : Starting Application on local.server.harbor with PID 32466 (/root/http2/springboot2-mvc/springboot2-mvc-1.0-exec.jar started by root in /root/http2/springboot2-mvc)
2023-08-31 15:39:54.420  INFO 32466 --- [           main] springboot2.mvc.Application              : No active profile set, falling back to default profiles: default
2023-08-31 15:39:54.504  INFO 32466 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@769c9116: startup date [Thu Aug 31 15:39:54 CST 2023]; root of context hierarchy
2023-08-31 15:39:55.653  INFO 32466 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https) 8082 (http)
2023-08-31 15:39:55.681  INFO 32466 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-08-31 15:39:55.681  INFO 32466 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2023-08-31 15:39:55.689  INFO 32466 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : Loaded APR based Apache Tomcat Native library [1.2.35] using APR version [1.4.8].
2023-08-31 15:39:55.690  INFO 32466 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
2023-08-31 15:39:55.690  INFO 32466 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
2023-08-31 15:39:55.694  INFO 32466 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : OpenSSL successfully initialized [OpenSSL 1.0.2k-fips  26 Jan 2017]
2023-08-31 15:39:55.768  INFO 32466 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-08-31 15:39:55.768  INFO 32466 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1268 ms
2023-08-31 15:39:55.895  INFO 32466 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2023-08-31 15:39:55.899  INFO 32466 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2023-08-31 15:39:55.899  INFO 32466 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2023-08-31 15:39:55.899  INFO 32466 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2023-08-31 15:39:55.899  INFO 32466 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]

Now test it:

[root@local webhook_service]# curl -k -vvv https://localhost:8443/getMapResult
* About to connect() to localhost port 8443 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
*   subject: CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx
*   start date: 8月 30 08:32:05 2023 GMT
*   expire date: 8月 27 08:32:05 2033 GMT
*   common name: xx
*   issuer: CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx
> GET /getMapResult HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8443
> Accept: */*
>
< H2 200
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Thu, 31 Aug 2023 07:40:30 GMT
<
* Connection #0 to host localhost left intact



3. Summary

In this post, I demonstrated how to solve java.lang.UnsatisfiedLinkError when adding http/2 support for springboot applications . That’s it, thanks for your reading.