diff options
Diffstat (limited to 'libraries/common-0/TrapSecurityManager.java')
-rw-r--r-- | libraries/common-0/TrapSecurityManager.java | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/libraries/common-0/TrapSecurityManager.java b/libraries/common-0/TrapSecurityManager.java new file mode 100644 index 0000000..161b74f --- /dev/null +++ b/libraries/common-0/TrapSecurityManager.java @@ -0,0 +1,88 @@ +package cbt.reflect; + +import java.security.*; +/* +When enabled, this SecurityManager turns System.exit(...) calls into exceptions that can be caught and handled. +Installing a SecurityManager is a global side-effect and thus needs extra care in a persistent +background process like CBT's. The current approach is install it once during JVM-startup. +When disabled this delegates to the SecurityManager installed before if any, which +would be Nailgun's if running on Nailgun. If we do not delegate to Nailgun, it seems we +could in some cases kill the server process +*/ +public class TrapSecurityManager extends ProxySecurityManager { + public static ThreadLocal<Boolean> trapExitCode() { + // storing the flag in the installed security manager + // instead of e.g. a static member is necessary because + // we run multiple versions of CBT with multiple TrapSecurityManager classes + // but we need to affect the installed one + SecurityManager sm = System.getSecurityManager(); + if (sm instanceof TrapSecurityManager) { + return ((TrapSecurityManager) sm)._trapExitCode; + } else { + try { + @SuppressWarnings("unchecked") + ThreadLocal<Boolean> res = + (ThreadLocal<Boolean>) sm.getClass().getMethod("trapExitCode").invoke(null); + return res; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private final ThreadLocal<Boolean> _trapExitCode = + new ThreadLocal<Boolean>() { + @Override + protected Boolean initialValue() { + return false; + } + }; + + protected TrapSecurityManager(SecurityManager parent) { + super(parent); + } + + public void checkPermission(Permission permission) { + /* + NOTE: is it actually ok, to just make these empty? + Calling .super leads to ClassNotFound exteption for a lambda. + Calling to the previous SecurityManager leads to a stack overflow + */ + if (!TrapSecurityManager.trapExitCode().get()) { + super.checkPermission(permission); + } + } + + public void checkPermission(Permission permission, Object context) { + /* Does this methods need to be overidden? */ + if (!TrapSecurityManager.trapExitCode().get()) { + super.checkPermission(permission, context); + } + } + + // FIXME: we should probably choose a more unique name for this + private static final String prefix = "[TrappedExit] "; + + @Override + public void checkExit(int status) { + if (TrapSecurityManager.trapExitCode().get()) { + // using a RuntimeException and a prefix here instead of a custom + // exception type because this is thrown by the installed TrapSecurityManager + // but other versions of cbt need to be able to catch it, that do not have access + // to that version of the TrapSecurityManager class + throw new RuntimeException(prefix + status); + } + super.checkExit(status); + } + + public static boolean isTrappedExit(Throwable t) { + return t instanceof RuntimeException + && t.getMessage() != null + && t.getMessage().startsWith(prefix); + } + + public static int exitCode(Throwable t) { + assert (isTrappedExit(t)); + return Integer.parseInt(t.getMessage().substring(prefix.length())); + } +} |