Почему бросать исключение 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 должен обрабатывать любые исключения, которые он может сам по себе, и пусть результат будет известен вызывающему. Для меня анти-шаблон в описываемой вами ситуации использует исключения для передачи состояния объекта. Исключения должны быть исключениями, которые не используются как возвращаемые значения.