Проблема с перехватом RuntimeExceptions как NullPointerExceptions необработанным обработчиком исключений в приложениях Java FX

Я прочитал этот пост глобальная обработка исключений JavaFx 8 и попытался обработать неперехваченные исключения в мое приложение. Работает нормально, как описано в посте. Но когда я добавил оператор, вызвавший исключение NullPointerException, UncaughtExceptionHandler не перехватил это исключение. Почему ? Есть ли другой поток, обрабатывающий это исключение? Или мне нужно установить DefaultUncaughtExceptionHandler? Я прочитал JavaDocs:

Обработка неперехваченных исключений сначала контролируется потоком, затем объектом ThreadGroup потока и, наконец, обработчиком неперехваченных исключений по умолчанию. Если поток не имеет явного набора обработчиков необработанных исключений, а группа потоков потока (включая родительские группы потоков) не специализируется на своем методе uncaughtException, то будет вызван метод uncaughtException обработчика по умолчанию.

Я понятия не имею, как получить решение, которое обрабатывает все неперехваченные исключения. Вы можете помочь? Спасибо за вашу поддержку!!

Это код:

package TestSimpleDialog;

public class Main extends Application {
    private final Logger logger = Logger.getLogger(this.getClass().getName());
    private MyHandler myHandler = new MyHandler();
    @Override
    public void init() {
    // Thread.currentThread is the FX-Launcher thread:
    Thread.currentThread().setUncaughtExceptionHandler(myHandler);
    System.out.println(Thread.currentThread().getUncaughtExceptionHandler());
    try {
        logger.addHandler(new FileHandler("java.myLOG"));
    }
    catch (IOException e) {
        throw new IllegalStateException("IOException when adding File Handler");
    }
    }
    @Override
    public void start(Stage primaryStage) {
    logger.info("Test Application started");
    // Thread.currentThread() is the FX-Application thread:
    Thread.currentThread().setUncaughtExceptionHandler(myHandler);
    // If this thread has not had an uncaught exception handler explicitly set then this thread's ThreadGroup object
    // is returned, unless this thread has terminated, in which case null is returned.
    System.out.println(Thread.currentThread().getUncaughtExceptionHandler());

    //        try {
    //            URI uriTest = new URI(null);
    //        } catch (URISyntaxException e) {
    //            throw new IllegalStateException("URISyntaxException by testing");
    //        }

    StackPane root = new StackPane();
    Button button = new Button("Throw exception");
    button.setOnAction(event -> {
        throw new RuntimeException("** T E S T **") ;
    });
    root.getChildren().add(button);
    Scene scene = new Scene(root, 150, 60);
    primaryStage.setScene(scene);
    primaryStage.show();
    }

    public static void main(String[] args) {
    launch(args);
    }

    class MyHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        System.out.println("MyHandler caught exception: "+throwable.getMessage());
        logger.log(Level.SEVERE, "**TEST** threw an uncaught exception", throwable);
    }
    }
}

Когда я нажимаю кнопку, я получаю этот вывод на консоли:

TestSimpleDialog.Main$MyHandler@49285759
Aug. 08, 2020 5:55:33 NACHM. TestSimpleDialog.Main start
INFORMATION: Test Application started
TestSimpleDialog.Main$MyHandler@49285759
MyHandler caught exception: ** T E S T **
Aug. 08, 2020 5:55:51 NACHM. TestSimpleDialog.Main$MyHandler uncaughtException
SCHWERWIEGEND: **TEST** threw an uncaught exception
java.lang.RuntimeException: ** T E S T **
    at TestSimpleDialog.Main.lambda$start$0(Main.java:47)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher............

Но когда я активировал этот оператор, чтобы получить исключение NullPointerException

try {
    URI uriTest = new URI(null);
} catch (URISyntaxException e) {
    throw new IllegalStateException("URISyntaxException by testing");
}

Я мог видеть на консоли, что исключение не было перехвачено из-за отсутствия оператора MyHandler catchException: класс MyHandler печатает на Sysout. Кроме того, в файле журнала ничего не пишется.

TestSimpleDialog.Main$MyHandler@22b2aa29
TestSimpleDialog.Main$MyHandler@22b2aa29
Aug. 08, 2020 6:16:51 NACHM. TestSimpleDialog.Main start
INFORMATION: Test Application started
Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
    at java.base/java.net.URI$Parser.parse(URI.java:3104)
    at java.base/java.net.URI.<init>(URI.java:600)
    at TestSimpleDialog.Main.start(Main.java:41)
    at javafx.graphics/com.sun.javafx.application.............

person Frizi    schedule 08.08.2020    source источник
comment
хм ... не совсем точно: но похоже, что программа запуска обрабатывает все исключения (распечатывая трассировку стека). Если вы делаете то же самое в простой основной (не в приложении), вызывается обработчик.   -  person kleopatra    schedule 09.08.2020
comment
нет, вырежьте путем печати трассировки стека - он оборачивает все в исключение RuntimeException и выдает его;)   -  person kleopatra    schedule 09.08.2020
comment
Итак, вернемся к нулевому шагу: чего вы хотите достичь? Вы можете установить глобальный uncaughtExceptionHandler при запуске ..   -  person kleopatra    schedule 09.08.2020
comment
нет, не работает .. из идей (и слишком жарко в Центральной Европе, чтобы действительно все обдумать, извините ;)   -  person kleopatra    schedule 09.08.2020


Ответы (1)


У меня нет ответа на вопрос как — только предварительное объяснение почему (похоже, первая мысль в моих комментариях была не за горами ;)

В его основе лежит тот факт, что приложение создается посредством отражения: любые исключения, возникающие в процессе инициализации/запуска, всплывают как ошибки при создании экземпляра, а именно как InvocationTargetException. И они действительно обрабатываются LauncherImpl.launchApplicationWithArgs .. ex.printStackTrace

public static void launchApplicationWithArgs(final ModuleAccess mainModule,
        final String mainClassName,
        final String preloaderClassName, String[] args) {

  // invoke, handle exception, line 472
  ...
    } catch (InvocationTargetException ex) {
        ex.printStackTrace();
        abort(null, "Exception running application %1$s", tempAppClass.getName());
        return;
    }

Не вижу способа перехватить это (что может быть ошибкой... или нет).

Изменить

Для регистрации (помимо вывода ошибок) ошибок, объединенных в InvocationTargetException, можно поместить рабочую нагрузку метода init/start в блок try .. catch ... и вручную вызвать обработчик, что-то вроде

@Override
public void init() throws Exception {
    try {
        // do stuff that might be throwing
        throw new ArithmeticException("am I caught?");
    } catch (Exception ex) {
        // invoke the handler and re-throw
        myHandler.uncaughtException(Thread.currentThread(), ex);
        throw(ex);
    }

}
person kleopatra    schedule 09.08.2020
comment
Означает ли это, что в файл журнала ничего не записывается, а только в поток печати ошибок? Что насчет метода прерывания? - person Frizi; 13.08.2020
comment
хорошо, если обработчик не может поймать ошибку, то и регистратор внутри метода перехвата тоже не сможет;) что вы подразумеваете под abort? - person kleopatra; 13.08.2020
comment
так или иначе, до сих пор не совсем понимаю, чего вы хотите добиться? Если вас интересует только ведение журнала, может быть достаточно обернуть все в start/int в try/catch и войти в блок catch? - person kleopatra; 13.08.2020
comment
Да, меня интересует журнал. Тем временем я сделал упаковку в блоки try/catch. Но когда есть java.lang.ArithmeticException: / нулем, например, его ловит launchApplicationWithArgs. Это означает запись в поток печати ошибок. Вот и все, исключение не выдается и, следовательно, нет записи в файле журнала. Кстати, спасибо за помощь! - person Frizi; 13.08.2020
comment
Я только что заметил, что вы отредактировали свой ответ. Теперь я понимаю, что вы имеете в виду, говоря о заключении всего в start/ini. Поэтому я изменил свой код. Теперь он работает нормально для меня. - person Frizi; 16.08.2020