从API中防止System.exit()

如果遇到exception,我正在使用第三方库来执行System.exit() 。 我正在使用来自jar的API。 无论如何,我可以防止System.exit()调用,因为它导致我的应用程序closures? 删除System.exit()由于许多其他许可问题,我无法反编译和重新编译该jar。 我曾经遇到过一个答案[我不记得的其他问题],我们可以使用Java中的SecurityManager来做这样的事情。 如果有人能帮助我,这将是伟大的。

这里有一篇博文,

http://jroller.com/ethdsy/entry/disabling_system_exit

基本上它安装了一个安全pipe理器,用这里的代码禁用System.exit()

  private static class ExitTrappedException extends SecurityException { } private static void forbidSystemExitCall() { final SecurityManager securityManager = new SecurityManager() { public void checkPermission( Permission permission ) { if( "exitVM".equals( permission.getName() ) ) { throw new ExitTrappedException() ; } } } ; System.setSecurityManager( securityManager ) ; } private static void enableSystemExitCall() { System.setSecurityManager( null ) ; } 

编辑 : 马克斯在下面的评论中指出

从Java 6开始,权限名称实际上是“exitVM。”+状态,例如“exitVM.0”。

但是, exitVM.*权限指的是所有的退出状态, exitVM保留为exitVM.*的简写forms,所以上面的代码仍然有效(请参阅RuntimePermission的文档 )。

请参阅我的回复如何避免JFrame EXIT_ON_CLOSE操作退出整个应用程序? 。

编辑1:链接的源。 演示如何使用SecurityManager来防止System.exit(n)

 import java.awt.*; import java.awt.event.*; import java.security.Permission; /** NoExit demonstrates how to prevent 'child' applications from ending the VM with a call to System.exit(0). @author Andrew Thompson */ public class NoExit extends Frame implements ActionListener { Button frameLaunch = new Button("Frame"), exitLaunch = new Button("Exit"); /** Stores a reference to the original security manager. */ ExitManager sm; public NoExit() { super("Launcher Application"); sm = new ExitManager( System.getSecurityManager() ); System.setSecurityManager(sm); setLayout(new GridLayout(0,1)); frameLaunch.addActionListener(this); exitLaunch.addActionListener(this); add( frameLaunch ); add( exitLaunch ); pack(); setSize( getPreferredSize() ); } public void actionPerformed(ActionEvent ae) { if ( ae.getSource()==frameLaunch ) { TargetFrame tf = new TargetFrame(); } else { // change back to the standard SM that allows exit. System.setSecurityManager( sm.getOriginalSecurityManager() ); // exit the VM when *we* want System.exit(0); } } public static void main(String[] args) { NoExit ne = new NoExit(); ne.setVisible(true); } } /** This example frame attempts to System.exit(0) on closing, we must prevent it from doing so. */ class TargetFrame extends Frame { static int x=0, y=0; TargetFrame() { super("Close Me!"); add(new Label("Hi!")); addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent we) { System.out.println("Bye!"); System.exit(0); } }); pack(); setSize( getPreferredSize() ); setLocation(++x*10,++y*10); setVisible(true); } } /** Our custom ExitManager does not allow the VM to exit, but does allow itself to be replaced by the original security manager. @author Andrew Thompson */ class ExitManager extends SecurityManager { SecurityManager original; ExitManager(SecurityManager original) { this.original = original; } /** Deny permission to exit the VM. */ public void checkExit(int status) { throw( new SecurityException() ); } /** Allow this security manager to be replaced, if fact, allow pretty much everything. */ public void checkPermission(Permission perm) { } public SecurityManager getOriginalSecurityManager() { return original; } } 

使用SecurityManager来禁止System.exit()调用并不完美,至less有两个原因:

  1. 启用和不启用SecurityManager的Java应用程序是非常不同的。 这就是为什么它最终需要closures的原因,但不能用System.setSecurityManager(null)来完成。 这个调用将导致另一个安全权限检查,这将不可避免地失败,因为应用程序代码(SecurityManager子类)位于调用堆栈的顶部。

  2. 所有Java应用程序都是multithreading的,其他线程可以在forbidSystemExitCall()和enableSystemExitCall()之间做各种事情。 这些东西中的一些可以通过安全许可检查来保护,由于与上述相同的原因,这将会失败。 如果checkExit()被覆盖而不是[更通用的] checkPermission(),它将覆盖大多数的情况。

解决这个问题的唯一方法(我知道)是授予SecurityManager子类的所有特权。 它很可能要求它由一个单独的类加载器加载,例如bootstrap(null)类加载器。

前面的代码示例是部分正确的,但是我发现它最终阻止了我的代码访问文件。 为了解决这个问题,我写了一个有点不同的SecurityManager:

 public class MySecurityManager extends SecurityManager { private SecurityManager baseSecurityManager; public MySecurityManager(SecurityManager baseSecurityManager) { this.baseSecurityManager = baseSecurityManager; } @Override public void checkPermission(Permission permission) { if (permission.getName().startsWith("exitVM")) { throw new SecurityException("System exit not allowed"); } if (baseSecurityManager != null) { baseSecurityManager.checkPermission(permission); } else { return; } } } 

就我而言,我需要防止第三方库终止虚拟机。 但也有一些调用System.exit的grailstesting。 所以,我写了我的代码,以便它只在调用第三方库(不是普通事件)之前立即激活自定义安全pipe理器,然后立即恢复原来的安全pipe理器(如果有的话)。

这一切都有点难看。 理想情况下,我宁愿简单地删除System.exit代码,但我没有访问第三方库的源代码。