Throwable или Exception?

На днях придумал задачку и, как это бывает, попробовал решить ее. Есть процедура из двух частей. Если между первой и второй частью будет ошибка — нужно откатить первую во что бы то ни стало. Если ошибка произошла во время второй части, то нужно откатить вторую часть, а потом первую и никак иначе. В итоге родился try/catch монстр.

public void doAndForget() {
    try {
        service.doFirstPart();
        try {
            service.doSecondPart();
        } catch (Throwable t) {
            Log.warn(t);
            try {
                service.doRollbackSecondPart();
                try {
                    service.doRollbackFirstPart();
                } catch(Throwable t1) {
                    Log.error(t1);
                }
            } catch (Throwable t1) {
                Log.error(t1);
            }
        }
    } catch (Throwable t) {
        Log.warn(t);
        try {
            service.doRollbackFirstPart();
        } catch (Throwable t1) {
            Log.error(t1);
        }
    }
}


Почему Throwable, а не Exception? Боюсь. Вдруг какая-нибудь либа внутри кинет NoClassDefFoundError или AssertionError, тогда мы не откатимся. Да, вроде как, Error — это ошибки, которые даже не надо пытаться исправить, но я хочу исправить не ошибку, а то, что сделал мой код до нее. Да, вроде как, если упадет OutOfMemoryError, то текущий Thread должен просто умереть, но я хочу хотя бы попытаться восстановить состояние приложения.


Что скажут противники catchThrowable? Везде расставить catchException?
  • Вопрос задан
  • 7441 просмотр
Пригласить эксперта
Ответы на вопрос 4
@Apx
Java/JavaEE/Javascript developer
А почему не упростить и не убрать кучу ненужных try?
public void doAndForget() {
     boolean firstOk = false;
     boolean secondOk = false;
     try{
          firstOk = service.doFirst();
          if (firstOk){ //Если есть какая то жесткая зависимость исхода выполнения
               secondOk = service.doSecond();
          }
     } catch(Чего угодно){
          Log.warn("Status 1 task: "+ firstOk+"; 2nd task "+secondOk;
     } finally {
          //Проверить флаги и вызвать откаты в нужной последовательности
     }

}

И всё же лучше не пытаться поймать какие то NoClassDefFound или там java.lang.OutOfMemoryError: PermGen space, в последнем случае там уже в памяти может быть всякая срань, не доработавший gc или что то. Данные могут быть уже не валидны или неверны.
Ответ написан
pletinsky
@pletinsky
Я не разработчик java — поэтому прошу прощения заранее, но в данном языке вроде есть finally блок, который придумали в том числе для организации роллбеков. Зачем Вам перехватывать ошибки мне вообще не понятно.

И судя по формулировки задачи try/finally блоков должно быть 2, а не 5 как у вас.

А по поводу собственно вопросов — не забывайте, что бывают ситуации, в которых вы все равно теряете уверенность в том, что вам удасться сделать rollback — как раз например OutOfMemoryError — и ваши действия могут привести к неожиданному результату. Приложение должно быть контролируемым.
Ответ написан
@relgames
Java Developer
Можно применить немного функционального подхода и сделать, например, так
    public interface Transaction {
        void commit();
        void rollback();
    }

    public static void tryTransaction(Transaction tr) {
        try {
            tr.commit();
        } catch (Exception e) {
            //log.error....
            tr.rollback();
            throw new CommitException(...);
        }
    }


И вот как это использовать:
        tryTransaction(new Transaction() {
            @Override
            public void commit() {
                //step1
                //...

                //step2
                tryTransaction(new Transaction() {
                    ...
                });
            }

            @Override
            public void rollback() {
            }
        });


Тогда цепочка откатов выстроиться сама. Если ошибка случится на этапе rollback, то Exception просто передастся выше.
Ответ написан
Комментировать
Beholder
@Beholder
В руководствах по Java обычно пишется, что перехватывать Error и его субклассы не стоит, так как если их кто-то выкинул, то это означает, что система (JVM или программа) уже на этот момент сломана настолько, что восстанавливаться бесполезно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы