Catch an exit instruction by a library
To my as开发者_如何学Ctonishment and horror, I've just encountered the line System.exit(1);
in a library I use. I'm planning on contacting the library's authors and asking what gives, but meanwhile is there any way to prevent the library from killing my code (and worse, killing the application using my code)?
Perhaps somehow force the library to throw a SecurityException
, which I see that exit(int)
may throw?
There is only one way I know of, and that is to code a SecurityManager
which disallows the use of System.exit.
public class MySecurityMgr extends SecurityManager {
...
@Override
public void checkExit(int status) {
throw new RuntimeException(Integer.toString(status));
}
}
I think your only choice (short of whacking the author with wet towels until he rewrites the damn thing) would be to change the library's byte code.
The pretty approach is to use AspectJ with load time weaving when you load the library, the ugly option is to use a tool like asm to either delete or change the method call.
Here's an AspectJ aspect that deflects calls to System.exit()
:
public aspect SystemExitEvader{
pointcut systemExitCall() : call(* java.lang.System.exit(*));
void around() : systemExitCall(){
System.out.println("Call to System.exit() attempted");
// note: there is no call to proceed()
}
}
OK, I've seen the other answers about SecurityManagers and I agree that's probably the way to go, but I'll leave my answer here as an alternative.
Yes, you can install a SecurityManager
that overrides checkExit
.
I've found this particularly useful for unittesting code to make sure that tested code doesn't exit with 0, indicating spurious success before the tests have a chance to run to completion.
This won't help though if the SecurityException
ends up killing some normally long-lived essential thread.
yes security manager can do the trick
// install security manager to avoid System.exit() call from lib
SecurityManager previousSecurityManager = System.getSecurityManager();
final SecurityManager securityManager = new SecurityManager() {
@Override public void checkPermission(final Permission permission) {
if (permission.getName() != null && permission.getName().startsWith("exitVM")) {
throw new SecurityException();
}
}
};
System.setSecurityManager(securityManager);
try {
// Call here your lib code
} catch (SecurityException e) {
// Say hi to your favorite creator of closed source software that includes System.exit() in his code.
} finally {
System.setSecurityManager(previousSecurityManager);
}
精彩评论