Предотвращение System.exit () из API - PullRequest
32 голосов
/ 23 марта 2011

Я использую стороннюю библиотеку, которая делает System.exit(), если встречаются исключения.Я использую API из банки.В любом случае, я могу предотвратить вызов System.exit(), потому что это приводит к закрытию моего приложения?Я не могу декомпилировать и перекомпилировать jar после удаления System.exit() из-за множества других проблем с лицензированием.Однажды я нашел ответ [на какой-то другой вопрос, который я не помню] в stackoverflow, что мы можем использовать SecurityManager в Java, чтобы сделать нечто подобное.

Ответы [ 4 ]

33 голосов
/ 23 марта 2011

Здесь есть запись в блоге,

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 ) ;
  }

Редактировать : Макс указывает в комментариях ниже, что

начиная с Java 6, имя разрешенияна самом деле "exitVM." + status, например, "exitVM.0".

Однако разрешение exitVM.* относится ко всем состояниям выхода, а exitVM сохраняется как сокращение для exitVM.*поэтому приведенный выше код по-прежнему работает (см. документацию для RuntimePermission).

6 голосов
/ 23 марта 2011

См. Мой ответ на Как избежать операции 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 голосов
/ 16 февраля 2016

Предыдущий пример кода частично верен, но я обнаружил, что он блокировал доступ моего кода к файлам. Чтобы обойти эту проблему, я написал свой 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, но у меня нет доступа к исходному коду сторонней библиотеки.

3 голосов
/ 08 июля 2014

Использование SecurityManager для запрета вызовов System.exit () не идеально, по крайней мере по двум причинам:

  1. Java-приложения, работающие с включенным SecurityManager и без него, сильно различаются. Вот почему его необходимо в конце концов отключить, но это невозможно сделать с помощью System.setSecurityManager (null). Этот вызов приведет к другой проверке разрешений безопасности, которая неизбежно приведет к сбою, поскольку код приложения (подкласс SecurityManager) находится на вершине стека вызовов.

  2. Все Java-приложения являются многопоточными, и другие потоки могут выполнять различные действия между protectidSystemExitCall () и enableSystemExitCall (). Некоторые из этих вещей могут быть защищены проверками разрешений безопасности, которые не будут выполнены по тем же причинам, что и описанные выше. Если checkExit () переопределен вместо [гораздо более общего] checkPermission (), он будет охватывать большинство случаев.

Единственный способ (который я знаю) решить эту проблему - предоставить все привилегированные подклассу SecurityManager. Весьма вероятно, что он будет загружен отдельным загрузчиком классов, например, загрузчик загрузочных классов (null).

...