nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.39k stars 327 forks source link

Java: SpringBoot3 - Unable to load Context #869

Open tippexs opened 1 year ago

tippexs commented 1 year ago

On Unit 1.29.1 - Java17 - Docker eclipse-temurin:17-jdk-jammy

It is not possible to load a SpringBoot application version >=3.0. Unit did not crash. It simply isn't loading the Spring Application context. The log initially shows:

2023/05/06 01:34:53 [info] 1#1 unit 1.30.0 started
2023/05/06 01:34:53 [info] 76#76 discovery started
2023/05/06 01:34:53 [notice] 76#76 module: java 17.0.7 "/usr/lib/unit/modules/java.unit.so"
2023/05/06 01:34:53 [info] 1#1 controller started
2023/05/06 01:34:53 [notice] 1#1 process 76 exited with code 0
2023/05/06 01:34:53 [info] 78#78 router started
2023/05/06 01:34:53 [info] 78#78 OpenSSL 3.0.2 15 Mar 2022, 30000020
2023/05/06 01:34:53 [info] 79#79 "java" prototype started
2023/05/06 01:34:53 [info] 80#80 "java" application started

Sending request to the HelloWorldController which should answer with a nice Hello from Spring on Unit! is displaying a directory index like

t.stark@C02FN35FMD6R ~/w/u/p/docker (master)> curl -v localhost:8090
*   Trying 127.0.0.1:8090...
* Connected to localhost (127.0.0.1) port 8090 (#0)
> GET / HTTP/1.1
> Host: localhost:8090
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Last-Modified: Sat, 06 May 2023 01:34:53 GMT
< Server: Unit/1.30.0
< Date: Sat, 06 May 2023 02:59:28 GMT
< Transfer-Encoding: chunked
<
<a href="WEB-INF">WEB-INF</a><br>
<a href="org">org</a><br>
<a href="META-INF">META-INF</a><br>
* Connection #0 to host localhost left intact

Starting unitd in debug mode shows the reason:

2023/05/06 02:45:12.831 [debug] 0#573 [unit] Context.wsSession.test: Endpoint instance registration failed
2023/05/06 02:45:13.379 [debug] 0#573 [unit] Context.loadInitializer: initializer: nginx.unit.websocket.server.WsSci
2023/05/06 02:45:13.384 [debug] 0#573 [unit] Context.loadInitializer: find handles: javax.websocket.server.ServerEndpoint
2023/05/06 02:45:13.385 [debug] 0#573 [unit] Context.loadInitializer: find handles: javax.websocket.server.ServerApplicationConfig
2023/05/06 02:45:13.385 [debug] 0#573 [unit] Context.loadInitializer: find handles: javax.websocket.Endpoint
2023/05/06 02:45:13.385 [debug] 0#573 [unit] Context.loadInitializer: no handles implementations
2023/05/06 02:45:13.386 [debug] 0#573 [unit] Context.load initializer(s)
2023/05/06 02:45:13.424 [debug] 0#573 [unit] Context.loadInitializer: initializer: org.apache.jasper.servlet.JasperInitializer
2023/05/06 02:45:13.424 [debug] 0#573 [unit] Context.loadInitializer: no HandlesTypes annotation
2023/05/06 02:45:13.428 [debug] 0#573 [unit] Context.URLPattern: '*.jsp' is a suffix pattern
2023/05/06 02:45:13.429 [debug] 0#573 [unit] Context.URLPattern: '*.jspx' is a suffix pattern

As Spring >= requires Tomcat10 with Servlet 5 it is not longer compatible with Units Servlet Implementation. Looking at the Changes from Apache Tomcat 8 to 10 shows that they moved from javax to jakarta.

It would be necessary to implement a new Servlet Spec to be able to load the Context of the newer generation of Spring Boot applications for example.

Please consider this issue as documentation only - we are not in a hurry to fix this for now. I just wanted to find a place to document my findings.

An application to reproduce is attached.

spring3-0.0.1-SNAPSHOT.war.zip

The unit configuration

{
  "listeners": {
    "*:80": {
      "pass": "applications/java"
    }
  },

  "applications": {
    "java": {
      "type": "java",
      "webapp": "spring3-0.0.1-SNAPSHOT.war",
      "working_directory": "/var/www/"
    }
  }
}  
gdufrene commented 6 days ago

Hi there,

I've done some tests and investigations on that topic. I'm now able to start a basic spring boot 3.3.4 application over java 17 with nginx unit.

Changing all references from javax.servlet to Jakarta.servlet is not the major problem, even if some deprecated methods does not exist anymore. ClassGraph library encounter some problem to list classes in the extracted war because of java module system. I had to create a module-info.java that references dependencies in unit java jar. My spring java application also now have to care about module requirements.

Some minor changes were also needed in makefile / autoconf to split classpath and modulepath, more jars needed at compile time because of "requires" declaration on unit jar.

The major issue I encountered is that sometimes, when I call PUT rest endpoint to load my application war, the process to load the application freeze, sometimes before extracting the war, sometimes just after extracting it... no clue why.

I have to kill the "prototype" and "application" process, kill the main unit process, rm the config.json in state dir, rm all references to module-info classes and extracted webapp directory in $TMPDIR, then try again ...

Changes made : https://github.com/nginx/unit/compare/master...gdufrene:unit:fix/spring3

Many TODOs to resolve around WebSocket and Multipart

best regards.

callahad commented 4 days ago

@gdufrene that branch looks fantastic; are you planning to continue working on it and propose a pull request here? We'd gladly review it or try to assist in debugging.