> On Apr 21, 2026, at 11:30:34, Christopher Schultz 
> <[email protected]> wrote:
> 
> Konstantin,
> 
> On 4/20/26 3:08 PM, Konstantin Kolinko wrote:
>> пн, 20 апр. 2026 г. в 20:12, Christopher Schultz 
>> <[email protected]>:
>>> 
>>> All,
>>> 
>>> I just observed something in production that caused all kinds of weirdness.
>>> 
>>> java.lang.NullPointerException: Cannot invoke
>>> &quot;java.io.File.toURI()&quot; because &quot;base&quot; is null
>>> org.apache.jasper.JspCompilationContext.createOutputDir(JspCompilationContext.java:676)
>>>     at
>>> org.apache.jasper.JspCompilationContext.getOutputDir(JspCompilationContext.java:196)
>>>     at
>>> org.apache.jasper.JspCompilationContext.getClassFileName(JspCompilationContext.java:530)
>>>     at org.apache.jasper.compiler.Compiler.isOutDated(Compiler.java:441)
>>>     at org.apache.jasper.compiler.Compiler.isOutDated(Compiler.java:412)
>>>     at
>>> org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:584)
>>>     at
>>> org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:379)
>>>     at
>>> org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:356)
>>>     at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:307)
>>>     at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
>>> 
>>> 
>>> It looks like the value of javax.servlet.context.tempdir became null at
>>> some point.
>> Hi, Chris!
>> Looking at the code (e.g. in Tomcat 11),
>> 1) the "base" in org.apache.jasper.JspCompilationContext.createOutputDir
>> is "File base = options.getScratchDir();"
>> 2) the "options" in org.apache.jasper.servlet.JspServlet.init() are
>>             // Use the default Options implementation
>>             options = new EmbeddedServletOptions(config, context);
>> (Those can be reconfigured to use a custom class, but it is unlikely
>> that you use that.)
> 
> I have nothing custom; I'm using a standard Tomcat out of the box. Only 
> standard changes to server.xml for <Connector>s.
> 
>> 3) org.apache.jasper.EmbeddedServletOptions constructor validates the value
>> and logs with "fatal" severity if the value is null or not an existing
>> directory:
>>         if (scratchDir == null) {
>>             log.fatal(Localizer.getMessage("jsp.error.no.scratch.dir"));
>>             return;
>>         }
>>         if (!(scratchDir.exists() && scratchDir.canRead() &&
>> scratchDir.canWrite() && scratchDir.isDirectory())) {
>>             log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir",
>> scratchDir.getAbsolutePath()));
>>         }
>> BTW, seeing a "return" above is odd, as this is in a constructor and
>> thus it skips processing of all other configuration options.
>> The scratchDir field in EmbeddedServletOptions is final, so there is
>> no way to change it afterwards, so you should have seen the log
>> message from EmbeddedServletOptions constructor.
>> 4) Looking at o.a.c.core.StandardContext.postWorkDirectory()
>>         if (!dir.mkdirs() && !dir.isDirectory()) {
>>             log.warn(sm.getString("standardContext.workCreateFail",
>> dir, getName()));
>>         }
>>         ...
>>         context.setAttribute(ServletContext.TEMPDIR, dir);
>>         context.setAttributeReadOnly(ServletContext.TEMPDIR);
>> The "dir" in a "dir.mkdirs()" call cannot be null, and the last line
>> prevents further changes to the value.
>> Thus my question about a stray "return" in point 3) becomes moot, as
>> it should not happen.
>> Yet you saw something.
> 
> Yup.
> 
>> May it be that you are using some framework that mocks, wraps,
>> "instruments" the mentioned classes?
>> All is odd, given that the srratchDir field in EmbeddedServletOptions is 
>> final.
> 
> I'm not using JSPs much at all. I think one thing that may explain what's 
> happening specifically above is that we saw a file-upload component failing 
> due to a null temp dir, and only then did I launch a JSP to inspect the 
> servlet context. (We have a very small number of JSPs deployed, so it's not 
> entirely surprising that the JSP engine might not be loaded at all until I 
> tried this test, after knowing the temp dir was null).
> 
>> (A weirdness similar to an issue with JavaMelody, whatever that tool
>> is. E.g. oct 2025:
>> https://lists.apache.org/thread/v40g4opbcgs2s5ncdsl5qwg1l720oyfx
>> What is your version of Tomcat?
> 
> 9.0.108
> 
>> Any other oddities in the logs?
> 
> Nothing. I looked for a failure to discover the temp and/or work directories, 
> and I see nothing in catalina.out. (The log file goes back months, and our 
> most recent Tomcat restart was in March, while we do reload the application 
> periodically if we deploy a hot fix without restarting the container).
> 
> The work directory is there, has the correct ownership, etc. and contains 
> files from both before and after the mishap.
> 
>> Were there context starts/stops/restarts at the same time?
> 
> Not that we know of. Certainly not after we started investigating. This was 
> not a single error thrown a single time, either. Once the problem was 
> identified, it was trivially reproducible by loading any JSP.
> 
> Our fix was to restart the container and everything looks good, now.
> 
> So the root cause was that the temp dir servlet context attribute became 
> null, but I have no idea how that happened.
> 
> Unfortunately, I had to restart the container so I can't do any more 
> investigation at this point. :/


What JVM level? Could there be a bug in the ConcurrentHashMap implementation in 
that version?

Perhaps an intermittent failure of the CPU compare-and-swap instruction in some 
core? (Not very likely, but the first time I saw such a memory locking failure 
was at Bell Labs about 40 years ago, and I’ve encountered a few since.)

  - Chuck


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to