System.exit()
如果遇到异常,我正在使用第三方库。我正在使用 jar 中的 API。无论如何我可以阻止System.exit()
调用,因为它会导致我的应用程序关闭?System.exit()
由于许多其他许可问题,删除后我无法反编译和重新编译 jar 。我曾经在stackoverflow中遇到过一个[我不记得的其他问题]的答案,我们可以使用SecurityManager
Java来做这样的事情。
4 回答
这里有一篇博文,
http://jroller.com/ethdsy/entry/disabling_system_exit
基本上它安装了一个安全管理器,该管理器使用此处的代码禁用 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 ) ;
}
编辑:Max在下面的评论中指出
从 Java 6 开始,权限名称实际上是“exitVM.”+状态,例如“exitVM.0”。
但是,权限exitVM.*
是指所有退出状态,并exitVM
保留为 的简写exitVM.*
,因此上述代码仍然有效(请参阅文档RuntimePermission
)。
查看我对如何避免 JFrame EXIT_ON_CLOSE 操作退出整个应用程序的回复?.
编辑 1:链接的源。演示如何使用 aSecurityManager
来防止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 有点不同:
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;
}
}
}
就我而言,我需要防止第 3 方库终止 VM。但也有一些调用 System.exit 的 grails 测试。因此,我编写了我的代码,使其仅在调用 3rd 方库(不是常见事件)之前立即激活自定义安全管理器,然后立即恢复原始安全管理器(如果有)。
这一切都有点难看。理想情况下,我宁愿简单地删除 System.exit 代码,但我无权访问 3rd 方库的源代码。
使用 SecurityManager 来禁止 System.exit() 调用并不完美,至少有两个原因:
在启用和不启用 SecurityManager 的情况下运行的 Java 应用程序非常不同。这就是它最终需要关闭的原因,但不能使用 System.setSecurityManager(null) 来完成。此调用将导致另一个安全权限检查,这将不可避免地失败,因为应用程序代码(SecurityManager 子类)位于调用堆栈的顶部。
所有的 Java 应用程序都是多线程的,其他线程可以在 forbidSystemExitCall() 和 enableSystemExitCall() 之间做各种事情。其中一些东西可以通过安全权限检查来保护,这将由于与上述相同的原因而失败。如果 checkExit() 被覆盖而不是 [更通用的] checkPermission(),那么它将涵盖大多数情况。
解决此问题的唯一方法(我知道)是将所有特权授予 SecurityManager 子类。它很可能需要一个单独的类加载器来加载它,例如引导(空)类加载器。