JavaEE6: Comment protéger une application Web lorsque la base de données s'arrête


Tout d'abord, mon framework est Java EE 6 avec JSF, managed bean, EJB et JPA. J'écris un programme simple pour interroger les informations de la base de données. Ainsi, lorsque je clique sur un bouton, il déclenche un événement vers un bean géré, où une méthode d'écoute d'événement accède à la méthode EJB. La méthode EJB fera une simple requête select à une entité. Si la base de données est arrêtée avant ou pendant le temps I select, j'obtiens une exception

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

Comment puis-je me prémunir de cette exception? Certainement un try, catch ici, mais vous ne savez pas où le mettre. Lorsque je fais em.createNamedQuery ou em.remove, j'essaie de capter com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, mais j'ai une erreur a dit: Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement.

Ci-dessous sont mes codes, où pourrais-je attraper l'exception?

C'est mon EJB

@Stateless
@LocalBean
public class DocumentSBean {
    @PersistenceContext
    private EntityManager em;

    public List<User> listUser(){
         Query query = em.createNamedQuery("User.listUser");
         query.returnResultList();
    }
}

C'est mon ManagedBean

@ManagedBean(name="document")
@ViewScoped
public class DisplayListController implements Serializable {            

   @EJB
   DocumentSBean sBean;

   List<User> users = null;

   public void foo(){
       users = sBean.listUser();
   }
}

MODIFIER

J'essaie dans les deux sens comme indiqué ci-dessous, mais je retourne toujours le statut 200 au lieu de 500 sur firebug

<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities"/>

OU

<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities">
       <p:ajax actionListener="#{document.setDisplayFacility}" update="myform" event="click"/>
</p:commandButton>

Voici setDisplayFacility ()

public void setDisplayFacility(){
   facilities = sBean.getAllFacilities(); //sBean is EJB
   displayFacility = true;
}
Author: Thang Pham, 2010-11-10

3 answers

Comment puis-je me prémunir de cette exception? Certainement un essai, attraper ici, mais je ne sais pas où le mettre.

N'attrapez que l'exception là où vous pouvez la gérer de manière raisonnable.


J'essaie d'attraper com.mysql.jdbc.exception.jdbc4.CommunicationsException, mais j'ai eu une erreur: Exception com.mysql.jdbc.exception.jdbc4.CommunicationsException n'est jamais lancée dans le corps de l'instruction try correspondante.

Le CommunicationsException est dans ce cas un imbriqué exception de DatabaseException. EclipseLink est déjà sous les couvertures en train d'attraper le CommunicationsException et de le repenser en tant que DatabaseException avec le CommunicationsException comme cause première. Quelque chose comme:

try {
     // Execute query.
} catch (Exception e) {
     throw new DatabaseException("Internal Exception: " + e, e);
}

En théorie, vous ne pouvez le gérer que comme suit:

try {
     // Let EclipseLink execute query.
} catch (DatabaseException e) {
    if (e.getCause() instanceof CommunicationsException) {
        // Handle.
    }
}

Qui est cependant laid et non recommandé dans ce cas particulier.


Voici mes codes, où pourrais-je attraper l'exception?

Dépend de la façon dont vous souhaitez gérer l'exception. Si vous voulez l'afficher dans un générique page d'erreur, alors vous ne devriez pas l'attraper vous-même, mais juste le laisser aller. Le servletcontainer va alors l'attraper et le gérer lui-même. Il va rechercher la meilleure correspondance <error-page> dans le web.xml et l'afficher.

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/generic-error.xhtml</location>
</error-page>

Celui-ci affichera /generic-error.xhtmlpour toutes les sous-classes de java.lang.Exception.

Si vous voulez l'afficher dans un spécifiques page d'erreur, alors vous devez déclarer le <exception-type> plus spécifique pour correspondre au type d'exception. Par exemple:

<error-page>
    <exception-type>org.eclipse.persistence.exceptions.DatabaseException</exception-type>
    <location>/database-error.xhtml</location>
</error-page>

Cependant, bien que non explicitement spécifié dans votre question, je sais sur la base de votre historique de questions que vous utilisez JSF avec PrimeFaces . Vous devez garder à l'esprit que PrimeFaces pas affichera la page d'erreur lorsque la demande initiale a été faite par ajax. Au lieu de cela, son gestionnaire de vue ajax a déjà capturé l'exception elle-même et il déléguera à la <p:ajaxStatus> composant dans la vue. Essayez d'ajouter ajax="false" au composant de commande PrimeFaces et vous verrez enfin la page d'erreur par défaut du servletcontainer s'afficher (ou l'une des vôtres si une page correspondante dans web.xml est trouvée).

Si vous souhaitez afficher une interface utilisateur JSF générique lorsque PrimeFaces a reçu une erreur ajax, utilisez la facette error de <p:ajaxStatus>. Par exemple

<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:[email protected]?subject=Ajax%20Error"><h:outputText value="If in vain, please contact support" /></h:outputLink>
        </h:panelGroup>
    </f:facet>
</p:ajaxStatus>

(il y a cependant un bug dans PrimeFaces 2.2 RC1 qui provoque l'échec de l'affichage de la facette d'erreur, cela fonctionne correctement dans PrimeFaces 2.1)

 6
Author: BalusC, 2010-11-16 23:06:56

À mon avis au moins, une base de données devenant indisponible est une erreur grave qui doit être gérée et généralement dès que POSSIBLE. L'approche générale sur la protection contre une telle situation est l'application de mécanismes de haute disponibilité et de basculement par le clustering de bases de données.

Si vous n'avez pas cette option cependant et que vous ne voulez pas que l'utilisateur voit une grosse exception, ce que vous pourriez essayer de faire est de l'acheminer dans une page conviviale lorsque cette erreur particulière se produit. Pour ce faire, mettez ce qui suit dans votre web.xml (en supposant que votre FacesServlet est mappé à *.face):

<error-page>
    <exception-type>com.mysql.jdbc.exceptions.jdbc4.CommunicationsException</exception-type>
    <location>/errors/error.faces</location>
</error-page>

Alors, dans l'erreur.page xhtml l'approche générale consiste à mettre un message convivial et largement apologétique indiquant à quel point l'administrateur se sent désolé pour les inconvénients de l'utilisateur...

En tout cas, traiter de telles situations en attrapant des exceptions et en n'agissant pas sur elles, soit en les relançant, soit en les traitant dans le bloc de capture-si possible, est généralement considéré comme une mauvaise pratique à des erreurs critiques peuvent aller non remarqué.

En ce qui concerne votre question "n'est jamais jeté dans le corps de l'instruction try correspondante", vous voudrez peut-être vérifier cet article .

Santé!

 2
Author: Lefteris Laskaridis, 2010-11-10 17:05:20

L'exception CommunicationsException est encapsulée dans une exception EclipseLink DatabaseException, qui est une exception d'exécution. Si vous utilisez JPA ou JTA, cela peut également être encapsulé dans une PersistenceException ou une TransactionRolledbackException. Alors, essayez d'en attraper un, ou dans le pire des cas RuntimeException. L'exception CommunicationsException sera dans la chaîne caused by.

EclipseLink essaiera automatiquement de reconnecter les connexions mortes, mais si votre base de données est en panne, cela échouera (vous pouvez voir l'erreur enregistrée 3 fois), vous devrez donc signaler une erreur à votre utilisateur.

 2
Author: James, 2010-11-11 13:45:02