> 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 >>> "java.io.File.toURI()" because "base" 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]
