`fitnesse.slim.instructions.SystemExitSecurityManager` triggers `NoClassDefFoundError` issue
Describe the bug
fitnesse.slim.instructions.SystemExitSecurityManager triggers NoClassDefFoundError issues in a dockerized spring-boot SlimServer on JDK11.
Thanks to the prevent.system.exit=false system property, there is a workaround.
Fitnesse is included as maven dependency in my spring-boot app.
<fitnesse.version>20220319</fitnesse.version>
<spring-boot.version>2.6.7</spring-boot.version>
<java.version>11</java.version>
<dependency>
<groupId>org.fitnesse</groupId>
<artifactId>fitnesse</artifactId>
<version>${fitnesse.version}</version>
</dependency>
Workaround
In my docker build, I have the option to add an environment variable JAVA_OPTS -Dprevent.system.exit=false
to skip fitnesse.slim.instructions.SystemExitSecurityManager.
Or else dynamic class loading fails with NoClassDefFoundError in a dockerized spring-boot SlimServer.
Stack Trace
__EXCEPTION__:java.lang.NoClassDefFoundError: fitnesse/slim/SlimSymbol
at fitnesse.slim.VariableStore.replaceSymbolsInString(VariableStore.java:80) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimExecutionContext.create(SlimExecutionContext.java:51) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.StatementExecutor.create(StatementExecutor.java:88) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.instructions.MakeInstruction.executeInternal(MakeInstruction.java:26) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.instructions.Instruction.execute(Instruction.java:29) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor$Executive.executeStatement(ListExecutor.java:49) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor$Executive.executeStatements(ListExecutor.java:43) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor.execute(ListExecutor.java:85) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.executeInstructions(SlimServer.java:82) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.processOneSetOfInstructions(SlimServer.java:75) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.tryProcessInstructions(SlimServer.java:62) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.serve(SlimServer.java:47) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
__EXCEPTION__:java.lang.NoClassDefFoundError: fitnesse/slim/MethodExecutionResults
at fitnesse.slim.StatementExecutor.getMethodExecutionResult(StatementExecutor.java:133) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.StatementExecutor.call(StatementExecutor.java:112) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.instructions.CallAndOptionalAssignInstruction.executeInternal(CallAndOptionalAssignInstruction.java:30) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.instructions.Instruction.execute(Instruction.java:29) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor$Executive.executeStatement(ListExecutor.java:49) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor$Executive.executeStatements(ListExecutor.java:43) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.ListExecutor.execute(ListExecutor.java:85) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.executeInstructions(SlimServer.java:82) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.processOneSetOfInstructions(SlimServer.java:75) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.tryProcessInstructions(SlimServer.java:62) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
at fitnesse.slim.SlimServer.serve(SlimServer.java:47) [jar:file:/app/app.jar!/BOOT-INF/lib/fitnesse-20220319.jar!/:20220319]
Can you share a sample Dockerfile and pom.xml so we can reproduce the problem?
On a side note: can you give some background why you create a sprint-boot application including FitNesse? In my understanding sprint-boot is a tool to easily create a Java command line application or web application with server. And FitNesse already is a standalone application that you can either use as a command line tool to run tests or as a web server. Why not put that directly in a docker container, what does sprint-boot add?
Thank you for your response.
It is a requirement for the organization's platform that all apps are build from a specific docker image in CI/CD pipeline. The app contains a SlimServer and a set of domain specific fixtures that depend on domain Spring beans.
Another app contains the HtmlSlimTestSystem and it sends the requests to the SlimServer.
The DockerFile is just a one liner with a specific builder docker image. The pom.xml includes the xml in the original post.
I am fine with closing this ticket. The ticket will serve as a note for future reference.
If I recall correctly sprint boot uses a special/custom classloader. I suspect something goes wrong in the interactions between the FitNesse and Spring classloading.