DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Java
  4. Gracefully Dealing With Errors in Galleria - Part 6

Gracefully Dealing With Errors in Galleria - Part 6

Markus Eisele user avatar by
Markus Eisele
·
Apr. 04, 12 · Interview
Like (0)
Save
Tweet
Share
3.75K Views

Join the DZone community and get the full member experience.

Join For Free
The Galleria Java EE 6 example app is growing . Today I am going to write about how to gracefully deal with errors. A lot has been done about user input validation already but there are still a lot of failure situations which are not handled but should be. If you are curious about what happened in the past look at the first parts of the series: The basics , running it on GlassFish, running it on WebLogic, testing it and enhanced security.

General Exception Mechanism
The application uses checked exceptions to communicate errors between the layers. The ApplicationException is the root of all possible business exceptions.


Those business exceptions communicate validation violations and all known errors between the domain and the presentation layer. The <domain>Manager (e.g. AlbumManger) classes in the galleria-jsf view project catch them and use the ExceptionPrecessor to populate the error messages to the view. The other kind of Exceptions that could occur between those two layers are RuntimeExceptions. Those get wrapped into an EJBException by the container and are also caught by the The <domain>Manager classes. Those generate a more general error message which is shown to the user.


I am not going to jump in on checked vs. unchecked exceptions here (Google a bit about it if you are curious). I tend to use checked exceptions when the application has a chance to recover from the error. Unchecked are thrown when something happens which isn't recoverable. That is the reason, I am not happy with the exception handling mechanism build in at the moment. I am going to get into this a little later.

What is missing? ViewExpired and more.
Seems as if everything is handled right now. But only on the first impression. Open the login screen and wait a bit and let your http session timeout. You are now greeted with a not so nice ViewExpired exception screen.


If you are trying this as a loged-in user you are simply redirected to the login page. Anyway, the same error page could come up for some other unexpected conditions in the presentation layer. So, let's fix this. Most obvious thing to do is to simply introduce a dedicated error-page.

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/viewExpired.xhtml</location>
</error-page>

Now you redirect your users to a dedicated page which could tell him/her something nice about workplace security and not leaving the app unattended for such a long time. This works for most of the applications out there. If you are willing to have some additional information on the page or simply want to catch more than one exception and handle them individually without having to configure them statically, you need something called an ExceptionHandler. This is new in JSF 2 and all you need to do is to implement an ExceptionHandler and it's factory. The factory itself is configured in the facex-config.xml because there isn't any annotation for it.

Open the faces-config.xml and add the following lines at the bottom: 

<factory>
     <exception-handler-factory>info.galleria.handlers.GalleriaExceptionHandlerFactory</exception-handler-factory>
 </factory> 

Now we are going to implement the GalleriaExceptionHandlerFactory in the dedicated package. The interesting method here is the:

@Override
   public ExceptionHandler getExceptionHandler() {
       ExceptionHandler result = parent.getExceptionHandler();
       result = new GalleriaExceptionHandler(result);
       return result;
   }

This is called once per request must return a new ExceptionHandler instance each time it's called. Here the the real ExceptionHandlerFactory is called and asked to create the instance, which is then wrapped in the custom GalleriaExceptionHandler class. This is where the real interesting stuff happens.

@Override
public void handle() throws FacesException {
    for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
        ExceptionQueuedEvent event = i.next();
        ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
        Throwable t = context.getException();
        if (t instanceof ViewExpiredException) {
            ViewExpiredException vee = (ViewExpiredException) t;
            FacesContext fc = FacesContext.getCurrentInstance();
            Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
            NavigationHandler nav =
                    fc.getApplication().getNavigationHandler();
            try {
                // Push some stuff to the request scope for later use in the page
                requestMap.put("currentViewId", vee.getViewId());
                nav.handleNavigation(fc, null, "viewExpired");
                fc.renderResponse();
 
            } finally {
                i.remove();
            }
        }
    }
    // Let the parent handle all the remaining queued exception events.
    getWrapped().handle();
}

 

Iterate over the unhandler exceptions using the iterator returned from getUnhandledExceptionQueuedEvents().iterator(). The ExeceptionQueuedEvent is a SystemEvent from which you can get the actual ViewExpiredException. Finally you extract some extra information from the exception and place it in request scope to access it via EL in the page later on. Last thing to do here for a ViewExpiredException is to use the JSF implicit navigation system ("viewExpired" is resolved to "viewExpired.xhtml") and navigate to the "viewExpired" page via the NavigationHandler. Don't forget to remove the handled exception in the finally block. You don't want this to be handled again by the parent exception handler. Now we have to create the viewExpired.xhtml page. Do this inside the galleria-jsf\src\main\webapp folder. 

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
                template="./templates/defaultLayout.xhtml"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                >
    <ui:define name="title">
        <h:outputText value="#{msg['Exception.page.title']}" />
    </ui:define>
 
    <ui:define name="content">
        <h:form>
            <h:outputText value="#{msg['Exception.page.message']}" />
            <p>You were on page #{currentViewId}.  Maybe that's useful.</p>
            <p>Please re-login via the <h:outputLink styleClass="homepagelink" value="#{request.contextPath}/Index.xhtml" ><h:outputText value="Homepage" /></h:outputLink>.</p>
        </h:form>
    </ui:define>
</ui:composition>

Please note that I added new message properties here, so you need to make sure to place them in galleria-jsf\src\main\resources\resources\messages.properties and translations.
Until now this obviously only handles one special instance of exception. You could extend it to handle others as well. Now that we have the basic mechanism in place you are free to do this.


Refactoring the RuntimeException handling
As I said, I am not happy with the way the application is handling RuntimeExceptions. Now that we have a nice central exception handling in place we can move those stuff around a bit and refactor the *Manager classes. Delete all those    catch (EJBException ejbEx) { blocks from all of them. We are going to take care of them in the GalleriaExceptionHandler in a minute. Simply add an another check to the GalleriaExceptionHandler and redirect the user to another page if any other exception than a ViewExpiredException is thrown.

// check for known Exceptions
           if (t instanceof ViewExpiredException) {
               ViewExpiredException vee = (ViewExpiredException) t;
               // Push some stuff to the request scope for later use in the page
               requestMap.put("currentViewId", vee.getViewId());
 
           } else {
               forwardView = "generalError";
 
               Locale locale = fc.getViewRoot().getLocale();
               String key = "Excepetion.GeneralError";
               logger.error(Messages.getLoggerString(key), t);
               String message = Messages.getString(key, locale);
               FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null);
               fc.addMessage(null, facesMessage);
           }

This approach has some advantages. It reduces the needed code in the *Manager classes and we finally have a central place to take care of those unrecoverable exceptions. This still is not very enterprise like. Imagine your first level support team needs to look after customers and they start complaining that the only message they get is a "GeneralError". That is not very helpful. You support team would need to escalate it and second or third level would need to check the logs and and and .. All this because of an error, that we could have know. First thing to do is to find out about the causing error. Parsing stack traces isn't big fun. Especially not of RuntimeExceptions that are wrapped in EJBExceptions and further on in FacesExceptions. Thank god for the Apache Commons ExceptionUtils. Open your galleria-jsf pom.xml and add them as dependency: 

<dependency>
          <groupId>commons-lang</groupId>
          <artifactId>commons-lang</artifactId>
          <version>2.6</version>
      </dependency>

Now you can start to examine the root cause: 

} else {
               forwardView = "generalError";
               // no known instance try to specify
 
               Throwable causingEx = ExceptionUtils.getRootCause(t);
               if (causingEx == null) {
                   causingEx = t;
               }
//...
 logger.error(Messages.getLoggerString(key), t);
requestMap.put("errorCode", errorCode);

Don't forget to also log the complete stack-trace (t and not only causingEx) here. In general it's a bad thing to let users know about exceptions. Nobody really wants to see errors happen (because we do hate making mistakes) and after all exception stack-traces could disclose sensitive information which you wouldn't like to see on a screen somewhere. So you need to find a way to display something meaningful to the user without disclosing too much. That is where the famous error-codes come into play. Use the root-cause exception as message key or make your own decisions on what effort you are going to put in here. It might be a system of categories of errors (db, interface systems, etc.) which give the first-level support a good hint about what was causing the error. I would stick to a simpler solution from the beginning. Simply generate a UUID for every caught exception and trace it to both the log and the UI. A very simple one could be the following.

String errorCode = String.valueOf(Math.abs(new Date().hashCode()));

This should also be added to the message properties and don't forget that you need another one for the generalError template. If slf4j would use the same message format than jdk logging does you would only need one property .. anyway:

Exception.generalError.log=General Error logged: {}.

Exception.generalError.message=A general error with id {0} occured. Please call our hotline.

Add this to the generalError.xhtml and see how the error code is passed to the message template.

<h:outputFormat  value="#{msg['Exception.generalError.message']}" >
               <f:param value="#{errorCode}"/>
           </h:outputFormat>


There is still a lot to improve on here. You could use the javax.faces.application.ProjectStage to lookup the current mode the application is running in. If you are running in ProjectStage.Development you could also put the complete stack-trace to the UI and make debugging live a bit easier. The following snippet is trying to get the ProjectStage from JNDI.

public static boolean isProduction() {
        ProjectStage stage = ProjectStage.Development;
        String stageValue = null;
        try {
            InitialContext ctx = new InitialContext();
            stageValue = (String) ctx.lookup(ProjectStage.PROJECT_STAGE_JNDI_NAME);
            stage = ProjectStage.valueOf(stageValue);
        } catch (NamingException | IllegalArgumentException | NullPointerException e) {
            logger.error("Could not lookup JNDI object with name 'javax.faces.PROJECT_STAGE'. Using default 'production'");
        }
        return ProjectStage.Production == stage;
    }

What about the 3-digit Http Error Pages?
That is another thing to take care of. All those remaining 3-digit http error codes which return one of those not nice looking error pages. The only thing to do this is to map them in the web.xml like shown in the following:

<error-page>
        <error-code>404</error-code>
        <location>/404.xhtml</location>
    </error-page>

 

You should make sure to have those mappings in place and present a meaningful error to your users. It should become best practice to always offer a way to navigate further from there. 

 

 

 

 

 

 

 

 

application Java EE Error message Error code Best practice app Property (programming)

Published at DZone with permission of Markus Eisele, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • The Path From APIs to Containers
  • How To Handle Secrets in Docker
  • Fargate vs. Lambda: The Battle of the Future
  • REST vs. Messaging for Microservices

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: