JavaEE6: How to safeguard web application when the database shut down

风流意气都作罢 提交于 2019-12-02 03:12:24

How do I safeguard away from this Exception? Definitely a try, catch here, but not sure where to put it.

Only catch the exception there where you can handle it in a reasonable manner.


I try to catch com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, but I got an error said: Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement.

The CommunicationsException is in this case a nested exception of DatabaseException. EclipseLink is under the covers already catching the CommunicationsException and rethrowing it as DatabaseException with the CommunicationsException as root cause. Something like:

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

In theory, you can only handle it as follows:

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

Which is however ugly and not recommended in this particular case.


Below are my codes, where would I catch the Exception?

Depends on how you would like to handle the exception. If you want to display it in a generic error page, then you should not catch it yourself, but just let it go. The servletcontainer will then catch and handle it itself. It will lookup the best matching <error-page> in the web.xml and display it.

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

This one will display /generic-error.xhtml for all subclasses of java.lang.Exception.

If you want to display it in a specific error page, then you need to declare the <exception-type> more specific to match the actual exception type. E.g.:

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

However, although not explicitly specified in your question, I know based on your question history that you're using JSF with PrimeFaces. You need to keep in mind that PrimeFaces will not display the error page when the initial request was made by ajax. Instead, its ajax view handler has already catched the exception itself and it will delegate to the <p:ajaxStatus> component in the view. Try adding ajax="false" to the PrimeFaces command component and you'll finally see the servletcontainer's default error page being displayed (or any of yours if a matching one in web.xml is found).

If you want to display some generic JSF UI when PrimeFaces received an ajax error, then use the error facet of <p:ajaxStatus>. E.g.

<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>

(there's however some bug in PrimeFaces 2.2 RC1 which causes displaying the error facet to fail, it works correctly in PrimeFaces 2.1)

In my view at least, a database becoming unavailable is a serious error that needs to be handled and usually ASAP. The general approach on safeguarding against such situation is enforcing high-avaialbility and failover mechanisms through database clustering.

If you don't have this option however nor you want the user to see a big-fat-exception, what you might try to do is route him in some user-friendly page when this particular error happens. To do so, put the following in your web.xml (assuming that your FacesServlet is mapped to *.faces):

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

Then, in the error.xhtml page the general approach is to put some user-friendly and largely apologetic message indicating how sorry the administrator feels for the user's inconvenience...

At any rate, dealing with such situations by catching exceptions and not acting upon them, either by re-throwing them or dealing with them in the catch block-if possible, is generally considered as a bad practice at critical errors might go un-noticed.

Regarding your "is never thrown in body of corresponding try statement" question you might want to check this article.

Cheers!

The CommunicationsException is wrapped in a EclipseLink DatabaseException, which is a runtime Exception. If you are using JPA or JTA, then this may also get wrapped in a PersistenceException, or a TransactionRolledbackException. So, try catching one of these, or worst case RuntimeException. The CommunicationsException will be in the caused by chain.

EclipseLink will automatically try to reconnect dead connections, but if your database is down, this will fail (you may see the error logged 3 times), so you will need to report an error to your user.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!