Я использую стороннюю библиотеку, которая выполняет System.exit()
, если она встречает исключения. Я использую API из банки. В любом случае, я могу предотвратить вызов System.exit()
, потому что это заставляет мое приложение отключиться? Я не могу декомпилировать и перекомпилировать банку после удаления System.exit()
из-за множества других проблем с лицензированием. Я однажды наткнулся на ответ [на другой вопрос, который не помню] в stackoverflow, что мы можем использовать SecurityManager
в Java, чтобы сделать что-то вроде этого.
Предотвращение использования System.exit() API
Ответ 1
Здесь есть запись в блоге,
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
).
Ответ 2
Посмотрите мой ответ на Как избежать операции 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;
}
}
Ответ 3
Использование SecurityManager для запрета вызовов System.exit() не является совершенным, по крайней мере по двум причинам:
-
Приложения Java, работающие с включенным и включенным SecurityManager, сильно отличаются. Поэтому в конечном итоге его необходимо отключить, но это невозможно сделать с помощью System.setSecurityManager(null). Этот вызов приведет к другой проверке разрешений безопасности, которая неизбежно завершится неудачей, поскольку код приложения (подкласс SecurityManager) находится в верхней части вызывающего стека.
-
Все приложения Java являются многопоточными, а другие потоки могут выполнять различные действия между forbidSystemExitCall() и enableSystemExitCall(). Некоторые из этих вещей могут быть защищены с помощью проверок разрешения безопасности, которые будут сбой по тем же причинам, что описаны выше. Если checkExit() переопределяется вместо [гораздо более общий] checkPermission(), он будет охватывать большинство случаев.
Единственный способ (я знаю) разрешить это - предоставить все привилегии подклассу SecurityManager. Это, скорее всего, потребует, чтобы он загружался отдельным загрузчиком классов, например. bootstrap (null) загрузчик классов.
Ответ 4
Предыдущий пример кода является частично правильным, но я обнаружил, что он блокировал мой доступ к файлам. Чтобы обойти эту проблему, я написал свой 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;
}
}
}
В моем случае мне нужно было запретить сторонней библиотеке завершать работу виртуальной машины. Но были также некоторые тесты Grails, которые вызывали System.exit. Итак, я написал свой код, чтобы он только активировал настраиваемый диспетчер безопасности непосредственно перед вызовом в стороннюю библиотеку (а не обычное событие), а затем сразу же восстановил первоначальный менеджер безопасности, если он есть, после.
Все это немного уродливо. В идеале я бы предпочел просто удалить код System.exit, но у меня нет доступа к исходному коду сторонней библиотеки.