NoExitSecurityManager breaks parallel builds
ksperling opened this issue · 10 comments
SystemNoExitGuard
and similar code in the GMaven plugin that tries to set a SecurityManager for a specific block of code is inherently not thread safe.
In practice what happens in my build when I run it multi-threaded is that a first groovy script execution installs the NoExitSecurityManager
, and an overlapping execution from another module will then see the NESM as the "existing" SecurityManager and restore it after it's done. The end result is that the overall Maven build hangs after it is finished when the plexus Launcher tries to call exit
:
at org.codehaus.gmaven.plugin.util.NoExitSecurityManager.deny(NoExitSecurityManager.java:37)
at org.codehaus.gmaven.plugin.util.NoExitSecurityManager.checkExit(NoExitSecurityManager.java:59)
at java.lang.Runtime.exit(Runtime.java:107)
at java.lang.System.exit(System.java:971)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:358)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:39)
at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:122)
at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:50)
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:39)
at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:122)
at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:50)
Caused by: java.lang.SecurityException: Use of System.exit() is forbidden
at org.codehaus.gmaven.plugin.util.NoExitSecurityManager.deny(NoExitSecurityManager.java:37)
at org.codehaus.gmaven.plugin.util.NoExitSecurityManager.checkExit(NoExitSecurityManager.java:59)
at java.lang.Runtime.exit(Runtime.java:107)
at java.lang.System.exit(System.java:971)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:364)
... 7 more
I'm not quite sure what the purpose of the NESM is in the first place... is System.exit
likely to be called by some groovy code that a build script author does not have control over?
If someone wants to put a literal System.exit()
into a groovy execution inside their pom.xml, they shouldn't be surprised if it causes the JVM to exit, so this doesn't seem like something the gmaven plugin would need to guard against. Saving/restoring the standard in/out/err file handles falls into the same category. Overall it seems way outside the scope of a small utility plugin like this to manipulate the global security manager and IO streams for the entire JVM.
The approaches I can see to resolve this are
- Make save/restore/noexit configurable (opt-out or opt-in) and document that it's not threadsafe
- Remove the whole save/restore/noexit code entirely
- Serialize all groovy execution via a global lock
- Use a global ref-count for managing adding / removing the NESM.
Personally I would tend towards (1) or (2). Approaches (3) and (4) could still be combined with (1). I can look at submitting a PR if there is an agreed approach.
So FTR I don't remember why I put NESM, may be a side-effect of shell or console integration. I'm included though remove it, unless I can remember why it was needed and/or useful.
I thought the reason was for a script used both in GMaven and invoked directly from command line might have exits in them for that latter purpose that you don't want to happen when invoked from former. Maybe a bit of a special case...
Looks like the current guard code makes sense primarily for the shell
and console
mojos where System.out seems to be replaced by some kind of ANSI-aware stream and a stream that redirects into the console GUI window respectively. Whether or not forbidding System.exit()
in those cases is much of a muchness I guess.
So I'd propose to add a guard
config parameter that defaults to false to the execute
mojo and will bypass the NESM and stream save/restore code unless guard == true
.
@keeganwitt I guess that's possible... you'd still have to deal with the SecurityException in some way though (e.g. by using a specific sub-class and catching it in the mojo) to simulate a clean-ish exit of the groovy script.
Seems for those sorts of things a more robust approach might be to optionally fork off a new JVM similar to surefire.
So I'm pretty sure its a side effect of integrating the console (and maybe shell) goals which have code that call System.exit, but I'll double check. For execute I don't see any reason its needed, especially since its being such a PITA. The protection is useful, as its really hard to debug when maven just appears to randomly exit, but given the issues with Java and how the SM is generally handled, it may not be worth the complexity.
update doesn't look like groovy:shell or groovy:console are presently calling System.exit, maybe it was something from early days. I'll prepare a PR that rips this stuff out.
If you can give #8 a whirl and see its behaving better.
NOTE: I don't normally use the MT options of Maven so I tend to not run into these problems but I get that folks like that feature, I just found it was too problematic ;-)
Sorry for the slow response, yes using a gmaven build off that branch fixes my issue 👍
(I had to change the litmus-testsupport
dependency to version 1.9, otherwise dependency resolution failed for me due to it's dependency junit-ext
which doesn't seem to be in any of the relevant repos.)
cool, will merge and move forward, a lot of deps need to be updated like litmus, but I didn't want to change too much at one time.
I bumped litmus-testsupport to 1.9, got this hooked up to travis-ci as well. will look at a 2.1 release shortly. Maybe then follow up with a 2.2 with larger changes to modernize some other dependencies.