Closed filiphr closed 6 months ago
The ServletContextHandler
has it's own temp directory.
That follows the servlet spec rules, and does not use any Server level behavior, if it's undeclared by the ServletContext, then it is null.
See ServletContextHandler.setTempDirectory(File)
.
Note that each ServletContext needs it's own temp directory per spec. So using the Server level temp directory isn't a good fit for this spec behavior.
Also note, that when you use new MultipartConfigElement((String)null)
that has it's own location (unrelated to the ServletContext temp directory) and is forced to be ""
if you pass null
(like you are doing).
Which is also a undefined location.
See https://github.com/jakartaee/servlet/blob/6.0.0-RELEASE/api/src/main/java/jakarta/servlet/MultipartConfigElement.java#L39-L44
@joakime sorry, I am not following, am I configuring something incorrectly or the notes were general notes?
Also note, that when you use
new MultipartConfigElement((String)null)
that has it's own location (unrelated to the ServletContext temp directory) and is forced to be""
if you passnull
(like you are doing).
We are doing this on purpose. We want to have multi part config and we want to use the defaults from the Server. Actually, according to the spec https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#_MultipartConfig it says
The location attribute of the
jakarta.servlet.annotation.MultipartConfig
and the<location>
element of the<multipart-config>
is interpreted as an absolute path and defaults to the value of thejakarta.servlet.context.tempdir
. If a relative path is specified, it will be relative to thetempdir
location. The test for absolute path vs relative path MUST be done viajava.io.File.isAbsolute
.
Do I need to configure the temp directory of the servlet? This used to work without issues with Jetty 11.
The jakarta.servlet.context.tempdir
attribute is configured by calling ServletContextHandler.setTempDirectory(File)
.
Which calls the ServletContextHandler.setAttribute(ServletContext.TEMPDIR, new File("/absolute/path/to/the/context/specific/temp/directory"))
Without calling that method, you have no temp directory for that ServletContext
, and no attribute jakarta.servlet.context.tempdir
.
This is what happens with the ServletContextHandler
.
Take for example when you use a WebAppContext
(and a full blown WAR or exploded webapp directory), the startup / initialization of the WEB-INF
behaviors sets up the temp directory for that WebAppContext
based on spec behaviors (this is done in the WebInfConfiguration
).
That is the behavior for ee10 environment (the behavior is slightly different for ee9/ee8, as it has to go through a ee9/ee8 nesting layer that doesn't exist for ee10)
Since you are using ServletContextHandler
you have no WEB-INF
behaviors, no jakarta.servlet.context.tempdir
, etc.
It is up to you to specify the temp directory by calling ServletContextHandler.setTempDirectory(File)
.
Note: each WebAppContext
(and ServletContextHandler
) has its own unique temp directory, it should not share the same directory (like what would exist at the Server level).
Here's another example, you have no default descriptor behaviors with ServletContextHandler
as well ...
In a new, unconfigured ServletContextHandler
, you have no servlets, no filters, no listeners, no servlet attributes, no session support, no security support, barebones mimetypes, etc.
If you create a WAR file with just an empty web.xml
and deploy it, you'll have a DefaultServlet
, a JettyJspServlet
, session configurations, session configuration, security configuration, some constraints, a bunch of servlet attributes, mime-types declared, error-page declarations, locale declarations, etc.
Basically what is declared in here -> https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/webdefault-ee10.xml
Using ServletContextHandler
puts all of this control in your hands, no magic, no defaults, no initialization, no bytecode scanning, no annotations, its up to you to specify the behaviors you want to use.
A proposal PR #11397 from @lorban has been submitted to attempt to address the contract issue discovered with Server.getContext().getTempDirectory().
Fixing that contract issues causes a new issue.
As each ServletContextHandler
to have it's own temp directory, that PR now makes the ServletContextHandler
temp directory invalid for cases with multiple ServletContextHandler
instances.
Note: WebAppContext
and its WebInfConfiguration
is already doing the right thing.
Do I need to configure the temp directory of the servlet? This used to work without issues with Jetty 11
Did you use ServletContextHandler or WebAppContext in Jetty 11? The later does more default behaviours.
Did you use ServletContextHandler or WebAppContext in Jetty 11?
We used the ServletContextHandler
in Jetty 11. The only thing we did during the upgrade was to change the package name. The example I shared was the minimal reproducible example I could create.
The code we are using with Jetty 11 is
@filiphr another point of difference between Jetty 11 and Jetty 12 is the changes in the servlet spec with regards to the jakarta.servlet.MultipartConfigElement
This section of javadoc was added in Servlet 6.0. https://github.com/jakartaee/servlet/blob/6.0.0-RELEASE/api/src/main/java/jakarta/servlet/MultipartConfigElement.java#L109-L110
See:
The default in the Servlet 5 spec (what is used in Jetty 11) if unconfigured behavior in Jetty 11 was to not use the File system temp location for multipart.
The default in Servlet 6 spec means that a File system temp location is now required if you don't configure the MultipartConfigElement.fileSizeThreshold
to -1 (to prevent the file temp from being required)
Thanks for pointing that out @joakime. That's a good thing to know and for our tests we can perhaps use that.
I tried it out. However, if I do not set a temp dir it still leads to the same NPE.
@lorban @joakime @filiphr interesting that you find a difference in behaviour between jetty-11 and jetty-12, which is something we need to resolve.
Your code worked in previous versions because the multipart parsing checked if there was a javax.servlet.context.tempdir
context attribute set but if not (which is the default case) defaulted to using java.io.tmpdir
instead. Each request remembered its temp files, and deleted them when the request exited, so there was no cross-contamination between contexts.
In jetty-12 multipart handling we have changed to using a call to ServletContextHandler.Context.getTempDirectory()
and expecting it to never return null. From your stacktrace, it seems that it is indeed returning null. We just need to ensure that it falls back to 'java.io.tmpdir' to fix this problem.
Jetty version(s) 12.0.5
Jetty Environment
ee10
Java version/vendor
(use: java -version)
Picked up JAVA_TOOL_OPTIONS: -Duser.language=en -Duser.country=US openjdk version "17.0.10" 2024-01-16 OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7) OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode)
OS type/version MacOS M3
Description
There is an NPE when
HttpServletRequest#getParts
is used for a manually configured Jetty server. The stacktrace is:Is there a reason why the
ContextHandler
is not usinggetServer().getContext().getTempDirectory()
? It is inhttps://github.com/jetty/jetty.project/blob/a683690678fd0323918bb5079341d10ad8979c89/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java#L1125-L1131
If the
ServerContext
is used there thennull
will not be returned since it does:https://github.com/jetty/jetty.project/blob/a683690678fd0323918bb5079341d10ad8979c89/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java#L948-L951
and
tmpDir
is never null:https://github.com/jetty/jetty.project/blob/a683690678fd0323918bb5079341d10ad8979c89/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java#L861
How to reproduce?
Run the following main class: