JavaEE6: как защитить веб-приложение при завершении работы базы данных
Прежде всего, мой фреймворк - Java EE 6 с JSF, управляемым компонентом, EJB и JPA. Я пишу простую программу для запроса информации из базы данных. Поэтому, когда я нажимаю кнопку, она вызывает событие для управляемого компонента, где метод прослушивателя событий будет обращаться к методу EJB. Метод EJB сделает простой select
запрос к сущности. Если база данных отключена до или во время select
Я получаю исключение
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 51,460 milliseconds ago. The last packet sent successfully to the server was 0 milliseconds ago.
Error Code: 0
Как уберечься от этого исключения? Определенно try, catch
здесь, но не уверен, где его поставить. Когда я делаю em.createNamedQuery
или же em.remove
Я пытаюсь поймать com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
, но я получил сообщение об ошибке: Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement
,
Ниже приведены мои коды, где бы я поймал исключение?
Это мое EJB
@Stateless
@LocalBean
public class DocumentSBean {
@PersistenceContext
private EntityManager em;
public List<User> listUser(){
Query query = em.createNamedQuery("User.listUser");
query.returnResultList();
}
}
Это мое ManagedBean
@ManagedBean(name="document")
@ViewScoped
public class DisplayListController implements Serializable {
@EJB
DocumentSBean sBean;
List<User> users = null;
public void foo(){
users = sBean.listUser();
}
}
РЕДАКТИРОВАТЬ
Я пробую оба способа, как указано ниже, но все равно возвращаю статус 200 вместо 500 в firebug
<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities"/>
ИЛИ ЖЕ
<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities">
<p:ajax actionListener="#{document.setDisplayFacility}" update="myform" event="click"/>
</p:commandButton>
Вот setDisplayFacility()
public void setDisplayFacility(){
facilities = sBean.getAllFacilities(); //sBean is EJB
displayFacility = true;
}
3 ответа
Как уберечься от этого исключения? Определенно попробуйте, поймайте здесь, но не уверены, где его поставить.
Только ловите исключение там, где вы можете справиться с этим разумным образом.
Я пытаюсь поймать com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, но я получил сообщение об ошибке: Исключение com.mysql.jdbc.exceptions.jdbc4.CommunicationsException никогда не добавляется в тело соответствующего оператора try.
CommunicationsException
в этом случае является вложенным исключением DatabaseException
, EclipseLink уже находится под прикрытием CommunicationsException
и бросить его как DatabaseException
с CommunicationsException
как первопричина. Что-то вроде:
try {
// Execute query.
} catch (Exception e) {
throw new DatabaseException("Internal Exception: " + e, e);
}
Теоретически вы можете справиться с этим только следующим образом:
try {
// Let EclipseLink execute query.
} catch (DatabaseException e) {
if (e.getCause() instanceof CommunicationsException) {
// Handle.
}
}
Что, однако, некрасиво и не рекомендуется в данном конкретном случае.
Ниже приведены мои коды, где бы я поймал исключение?
Зависит от того, как вы хотели бы обработать исключение. Если вы хотите отобразить его на общей странице ошибок, то вам не следует ловить его самостоятельно, а просто отпустить. Контейнер-сервлет сам поймает и обработает его. Это будет искать лучшее соответствие <error-page>
в web.xml
и отобразить это.
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/generic-error.xhtml</location>
</error-page>
Этот покажет /generic-error.xhtml
для всех подклассов java.lang.Exception
,
Если вы хотите отобразить его на странице с конкретной ошибкой, вам нужно объявить <exception-type>
более конкретно, чтобы соответствовать фактическому типу исключения. Например:
<error-page>
<exception-type>org.eclipse.persistence.exceptions.DatabaseException</exception-type>
<location>/database-error.xhtml</location>
</error-page>
Однако, хотя это не указано явно в вашем вопросе, я знаю по истории ваших вопросов, что вы используете JSF с PrimeFaces. Вы должны иметь в виду, что PrimeFaces не будет отображать страницу с ошибкой, когда первоначальный запрос был сделан ajax. Вместо этого его обработчик представления ajax уже перехватил само исключение и передаст его <p:ajaxStatus>
Компонент в представлении. Попробуйте добавить ajax="false"
к компоненту команды PrimeFaces, и вы, наконец, увидите страницу с ошибкой сервлетконтейнера по умолчанию (или любую вашу, если она совпадает с web.xml
найден).
Если вы хотите отобразить некоторый универсальный пользовательский интерфейс JSF, когда PrimeFaces получил ошибку AJAX, используйте error
грань <p:ajaxStatus>
, Например
<p:ajaxStatus>
<f:facet name="start"><h:graphicImage value="images/ajax-loader.gif" /></f:facet>
<f:facet name="success"><h:outputText value="" /></f:facet>
<f:facet name="error">
<h:panelGroup layout="block" styleClass="ui-message-error ui-widget ui-corner-all">
<h:outputText value="An error has occurred!" /><br />
<h:outputLink value="#" onclick="window.location.reload(true)"><h:outputText value="Please reload page and retry" /></h:outputLink><br />
<h:outputLink value="mailto:support@example.com?subject=Ajax%20Error"><h:outputText value="If in vain, please contact support" /></h:outputLink>
</h:panelGroup>
</f:facet>
</p:ajaxStatus>
(однако в PrimeFaces 2.2 RC1 есть некоторая ошибка, которая приводит к сбою отображения фасета ошибки, в PrimeFaces 2.1 он работает правильно)
CommunicationException заключено в EclipseLink DatabaseException, которое является исключением времени выполнения. Если вы используете JPA или JTA, то это также может быть включено в PersistenceException или TransactionRolledbackException. Итак, попробуйте перехватить одно из них или исключение RuntimeException в худшем случае. CommunicationsException будет в вызванной цепью.
EclipseLink автоматически попытается восстановить неработающие соединения, но если ваша база данных не работает, это не удастся (вы можете увидеть ошибку, зарегистрированную 3 раза), поэтому вам нужно будет сообщить об ошибке своему пользователю.
На мой взгляд, по крайней мере, недоступность базы данных является серьезной ошибкой, которую нужно обработать, и обычно КАК МОЖНО СКОРЕЕ. Общий подход к защите от такой ситуации заключается в применении механизмов высокой доступности и аварийного переключения через кластеризацию базы данных.
Однако если у вас нет этой опции, и вы не хотите, чтобы пользователь видел исключение с большим содержанием жира, вы можете попробовать направить его на какую-то удобную для пользователя страницу, когда произойдет эта конкретная ошибка. Для этого поместите в ваш web.xml следующее (при условии, что ваш FacesServlet сопоставлен с *.faces):
<error-page>
<exception-type>com.mysql.jdbc.exceptions.jdbc4.CommunicationsException</exception-type>
<location>/errors/error.faces</location>
</error-page>
Затем на странице error.xhtml общий подход состоит в том, чтобы поместить некоторое удобное для пользователя и в основном извиняющееся сообщение, указывающее, как администратор сожалеет о неудобствах для пользователя...
Во всяком случае, работа с такими ситуациями путем перехвата исключений и не воздействия на них, либо перебрасыванием, либо обработкой их в блоке перехвата, если это возможно, обычно считается плохой практикой, когда критические ошибки могут остаться незамеченными.,
Что касается вашего вопроса "никогда не добавляется в тело соответствующего оператора try", вы можете проверить эту статью.
Ура!