JavaEE6: как защитить веб-приложение при завершении работы базы данных - PullRequest
2 голосов
/ 10 ноября 2010

Прежде всего, мой фреймворк - это Java EE 6 с JSF, управляемым компонентом, EJB и JPA.Я пишу простую программу для запроса информации из базы данных.Поэтому, когда я нажимаю кнопку, она запускает событие для управляемого компонента, где метод прослушивателя событий будет обращаться к методу EJB.Метод EJB сделает простой запрос select к сущности.Если база данных отключена до или во время I 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 ]

6 голосов
/ 16 ноября 2010

Как уберечься от этого исключения?Определенно попробуйте, поймайте здесь, но не уверены, где его поставить.

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


Я пытаюсь поймать com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, но я получил сообщение об ошибке: Exception 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.
    }
}

Что, однако, уродливо и не рекомендуется в данном конкретном случае


Ниже приведены мои коды, где бы я мог поймать исключение?

Зависит от того, как вы обработаете исключение.Если вы хотите отобразить его на странице ошибок generic , то вам не следует ловить его самостоятельно, а просто отпустить.Контейнер-сервлет сам поймает и обработает его.Он найдет лучшее соответствие <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)

2 голосов
/ 11 ноября 2010

CommunicationException заключено в EclipseLink DatabaseException, которое является исключением времени выполнения. Если вы используете JPA или JTA, то это также может быть включено в PersistenceException или TransactionRolledbackException. Итак, попробуйте перехватить одно из них или исключение RuntimeException в худшем случае. CommunicationsException будет вызвано цепочкой.

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

2 голосов
/ 10 ноября 2010

По крайней мере, на мой взгляд, недоступность базы данных является серьезной ошибкой, которую необходимо обработать и, как правило, КАК МОЖНО СКОРЕЕ.Общий подход к защите от такой ситуации заключается в применении механизмов высокой доступности и отработки отказа посредством кластеризации баз данных.

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

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

Относительно вашего вопроса "никогда не вставляется в тело соответствующего оператора try", который вы, возможно, захотитеотметьте эту статью.

Ура!

...