Почему бросать исключение EJBException является "рекомендуемой" практикой?

Я продолжаю получать это "предложение" от многих разработчиков-разработчиков снова и снова. По моему опыту, я обнаружил, что EJBExceptions хорошо подходят для "конца света" с точки зрения экземпляра bean (например, когда что-то не так, что экземпляр bean не может восстановить сам по себе). Если экземпляр может восстановиться, я думаю, что лучше исключить исключение приложения.

Вот пример, который я встречаю снова и снова:

private SomeResource resource;
ejbCreate:
    resource = allocateResource(...);

omMessage:
    try {
    ...
    } catch (JMSException e) {
        throw new EJBException(e);
    }

ejbRemove:
    freeResource(resource);

IMHO - это антипаттерн, который вызывает утечку ресурсов.

EDIT. В частности, спецификация EJB говорит, что если bean выбрасывает исключение среды выполнения (и EJBException - исключение во время выполнения) из бизнес-метода, то bean отбрасывается без вызова ejbRemove на нем.

Является ли это подходящим примером для исключения EJBException? Каковы соответствующие случаи, когда EJBException следует бросить?

Ответ 1

Бросок EJBException рекомендуется спецификацией EJB (14.2.2 в EJB 3) в случаях, когда EJB не может восстановиться из исключения, с которым он сталкивается. Спецификация также говорит, что EJB может разумно разрешить (непроверенные) системные исключения для распространения в контейнере.

Я согласен с вашим чтением спецификации, что в таких случаях контейнерный метод обратного вызова жизненного цикла не будет вызываться контейнером, и, следовательно, ваша озабоченность в связи с тем, что любой ресурс-порядок, который обычно происходит в обратном вызове ejbRemove(), не будет происходят, и поэтому существует опасность утечки ресурсов.

Мой опыт заключается в том, что очень много сложных проблем возникает из-за отсутствия защитного кодирования. "Ситуация X не может произойти в хорошо управляемой системе, и если это произойдет, то вся система будет разбита, поэтому я не буду кодировать эту вероятность". Затем мы получаем "интересное" выравнивание звезд и ошибок оператора, а "не может случиться" происходит несколько раз подряд, а непредвиденные побочные эффекты приводят к действительно сложной диагностике проблем.

Следовательно, я бы сказал:

  • Сделайте все возможное, чтобы оставить экземпляр Bean в состоянии, в котором может работать следующий вызов бизнес-метода. Это может означать ловушку исключений и закрытие ресурсов, которые являются ошибками. В этом случае вы можете затем сказать вызывающим: "Извините, guv, этот запрос не сработал, но, может быть, если вы повторите попытку позже..." Я делаю это, выбрасывая исключение TransientException.
  • Если у вас нет важной домашней работы в ejbRemove, тогда вы можете разрешить SystemExceptions протаиться, но я не уверен, что это дружелюбно. Вы зависите от библиотеки и выбрасываете NullPointerException. Является ли это более надежным, чтобы поймать и реконструировать TransientException?
  • Если у вас есть важная хозяйственная работа, я думаю, вам нужно поймать все исключения и по крайней мере попытаться очистить. Затем вы можете выбрать rethrow EJBException или системное исключение, чтобы экземпляр был уничтожен, но по крайней мере вы попытались выполнить домашнее хозяйство.

Ответ 2

Контейнер по-прежнему может выполнять текущую транзакцию, даже если метод EJB вызвал исключение приложения. Если ваш метод EJB может вызывать исключение приложения, вам следует рассмотреть возможность использования EJBContext.setRollbackOnly() следующим образом:

public void method() throws ApplicationException {
    try {
    ...
    } catch (ApplicationException e) {
        _context.setRollbackOnly();
        throw e;
    }
}

Бросание исключения EJBException или исключение системы (непроверенное) означает, что контейнер автоматически откатится от транзакции. См.: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html

Если ваши системные EJB не рутифицируют ловушку и обрабатывают исключения приложений, тогда код, который их выбрасывает, может привести к частично завершенной операции, фиксирующей ее изменения.

Это может привести к труднодоступным ошибкам, потому что "пользовательский миф" метода EJB с CMP заключается в том, что он сопоставляет транзакцию, которая является атомарной. Частично полная бизнес-логика атомарно привязана к базе данных. Это очень запутанно, когда это происходит на практике. Статья Джоэля о несовместимых абстракциях: http://www.joelonsoftware.com/articles/LeakyAbstractions.html объясняет, почему.

Если ваша система имеет EJB, которые неправильно обрабатывают исключения приложений, логический ответ заключается в их исправлении, чтобы они выполнялись. Пока проблема не будет устранена, ваша команда может иметь разумную причину отсутствия нежелательных исключений приложений из EJB.

Ответ 3

Ресурсы утечки зависят от того, как эти ресурсы администрируются. Отброшенный bean получает сбор мусора, отказываясь от ссылки на ресурс, который получает мусор, собранный в свою очередь, когда больше ссылок не указывает на него. Если ресурс также хранится в коллекции с пустым корнем, он будет протекать, если в контейнере нет диспетчера пулов, который может обнаруживать незанятые ресурсы и перерабатывать его.

Я согласен с вами в том, что bean должен обрабатывать любые исключения, которые он может сам по себе, и пусть результат будет известен вызывающему. Для меня анти-шаблон в описываемой вами ситуации использует исключения для передачи состояния объекта. Исключения должны быть исключениями, которые не используются как возвращаемые значения.