Задать вопрос

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?
  • Вопрос задан
  • 7585 просмотров
Подписаться 3 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 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 или программа) уже на этот момент сломана настолько, что восстанавливаться бесполезно.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Похожие вопросы