It takes 15 minutes or more for POI to initialize its first workbook in Java 8 on Windows 10, in a Tomcat 8 instance. Based on interrupting the process in the debugger and looking at the stack, it is spending the time in the classloader, driven by xbeans.
Edit This has the feel of a classloader issue because when I implemented the workaround for the POI library (below), other classes started expressing the same issue.
Edit The stack trace looks most similar to this bug: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8022063
As you can see in jvisualvm, there is no classloader contention in the process thread taskExecutor-1.
I have a workaround, but it should not be necessary. The accepted answer will explain WHY this is happening.
EDIT: Workaround
During application initialization I added this line to the static class initializer for a configuration bean, to force the classloader to preload the classes and resources. It executes instantly there, and when the actual process is invoked it also executes instantly.
XSSFWorkbook preload = new XSSFWorkbook ();
The final workaround was to preload all of the jar files related to SSL, security, and POI. As near as I can tell, when loaded late in a spring batch context (may be a red herring), the classloader is attempting to validate the bouncycastle jar files with algorithms encoded in the bouncycastle classes, and is running everything in the SSL stack in interpreted mode.
Reflections reflections = new Reflections("org.bouncycastle", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> bouncyCastleClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("sun.security", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> sunSecurityClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("javax.ssl", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> javaSslClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("org.apache.poi", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> poiClasses = reflections.getSubTypesOf(Object.class);
POM excerpt:
<poi.version>4.0.1</poi.version>
<!-- apache poi / poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
Code:
public void beforeJob(JobExecution jobExecution) {
if (logger.isDebugEnabled()) {
workbookname = "debuginfo-" + System.currentTimeMillis() + ".xlsx";
logger.debug("Debug statistics dump file will be saved at [{}]", workbookname);
debugStatsDump = new XSSFWorkbook(); // This line takes several minutes to run, apparently in the classloader???
}
}