Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki Markup
{scrollbar}

Error handling for MyFaces Core 2.0 and later versions

...

Code Block
titlefaces-config.xml

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" 
              version="2.0">

    <!-- ... -->
    <factory>
        <!-- ... -->
        <exception-handler-factory>org.apache.myfaces.context.ExceptionHandlerFactoryImpl</exception-handler-factory>
        <!-- ... -->
    </factory>
    <!-- ... -->
</faces-config>

...

Code Block
titleExceptionHandlerFactoryImpl.java

public class ExceptionHandlerFactoryImpl extends ExceptionHandlerFactory
{

    @Override
    public ExceptionHandler getExceptionHandler()
    {
        return new SwitchAjaxExceptionHandlerWrapperImpl(
                new MyFacesExceptionHandlerWrapperImpl(new ExceptionHandlerImpl()) , 
                new AjaxExceptionHandlerImpl());
    }
}

...

Code Block
titleExceptionHandlerFactoryImpl.java

public class ExceptionHandlerFactoryImpl extends ExceptionHandlerFactory
{

    @Override
    public ExceptionHandler getExceptionHandler()
    {
        return new CustomExceptionHandlerWrapper(getWrapped().getExceptionHandler());
    }
}

...

Code Block
titleMyFacesExceptionHandlerWrapperImpl.java

public class MyFacesExceptionHandlerWrapperImpl extends ExceptionHandlerWrapper
{
    //...
    public void handle() throws FacesException
    {
       //... some custom code goes here ...
    }
}

...

MyFaces Core provide a custom ExceptionHandler to deal with exceptions and provide detailed information about it. Since 2.0.8/2.1.2 this is disabled on Production environments unless it enabled on web.xml file. Don't forget to provide your custom error page in this scenario, to prevent show more information than necessary.

Code Block
xml
xml

  <context-param>
    <param-name>org.apache.myfaces.ERROR_HANDLING</param-name>
    <param-value>true</param-value>
  </context-param>

  <!-- if you want to use a different resource template file than 
       "META-INF/rsc/myfaces-dev-error.xml" this param let you configure
       it. (since 1.2.4-SNAPSHOT and 1.1.6-SNAPSHOT)-->
  <context-param>
    <param-name>org.apache.myfaces.ERROR_TEMPLATE_RESOURCE</param-name>
    <param-value>META-INF/rsc/custom-dev-error.xml</param-value>
  </context-param>

Provide an error page

The default ExceptionHandler in Production stage or when myfaces error handling is disabled just throw If org.apache.myfaces.ERROR_HANDLING is set to "false", or if it is undefined and the project stage is "Development", the default ExceptionHandler just throws an exception. So you can still setup an error page by adding something like this in your web.xml file:

Code Block
xml
xml

	<error-page>
		<error-code>500</error-code>
		<location>/somepage.jsp</location>
	</error-page>

...

If this is not what you want, though, you can always disable or modify this error-handling with the following parameters:

Code Block
xml
xml

  <!-- if you want to disable the behaviour completely -->
  <context-param>
    <param-name>org.apache.myfaces.ERROR_HANDLING</param-name>
    <param-value>false</param-value>
  </context-param>
  <!-- if you are using myfaces + facelets don't forget to do this -->
  <context-param>
    <param-name>facelets.DEVELOPMENT</param-name>
    <param-value>false</param-value>
  </context-param>

  <!-- if you want to use a different resource template file than 
       "META-INF/rsc/myfaces-dev-error.xml" this param let you configure
       it. (since 1.2.4-SNAPSHOT and 1.1.6-SNAPSHOT)-->
  <context-param>
    <param-name>org.apache.myfaces.ERROR_TEMPLATE_RESOURCE</param-name>
    <param-value>META-INF/rsc/custom-dev-error.xml</param-value>
  </context-param>

  <!-- if you want to choose a different class for handling the exception - the error-handler needs to include a method handleException(FacesContext fc, Exception ex)-->
  <context-param>
    <param-name>org.apache.myfaces.ERROR_HANDLER</param-name>
    <param-value>my.project.ErrorHandler</param-value>
  </context-param> 

...

For define a custom template file:

Code Block
xml
xml

  <context-param>
    <param-name>org.apache.myfaces.ERROR_HANDLING</param-name>
    <param-value>true</param-value>
  </context-param>

  <context-param>
    <param-name>org.apache.myfaces.ERROR_TEMPLATE_RESOURCE</param-name>
    <param-value>META-INF/rsc/mycustom-template-error.xml</param-value>
  </context-param>

...

The info of the error in the jsf page can be found using:

Code Block

#{exceptionContext.cause} : Cause retrieved from the exception
#{exceptionContext.stackTrace} : Stack trace of the exception
#{exceptionContext.exception} : Exception handled by this page 
#{exceptionContext.tree} : Print the component tree of the page that cause the error
#{exceptionContext.vars} : Enviroment variables from the request

...

1 define an error handling web page in web.xml

Code Block
xml
xml

	<error-page>
		<error-code>500</error-code>
		<location>/ErrorDisplay.jsf</location>
	</error-page>

2 Create the error handler display. Due to a problem with the JSF 1.1 specification, the error handling page cannot use a <f:view> but must use a subview.

Code Block
xml
xml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:subview id="error"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:t="http://myfaces.apache.org/tomahawk"
    xmlns:h="http://java.sun.com/jsf/html">

<html>
<head>
	<meta content="no-cache" http-equiv="Cache-Control" />
	<meta content="no-cache" http-equiv="Pragma" />
	<title>CMS - Error</title>
        <t:stylesheet path="#{SessionBean.styleSheet}" />
	</head>
	<body>
	<h:form>
           :
           : set up the normal view
           :
	   <h:outputText styleClass="infoMessage" escape="false" value="#{ErrorDisplay.infoMessage}" />
	   <t:htmlTag value="br" />
	   <h:inputTextarea style="width: 99%;" rows="10" readonly="true" value="#{ErrorDisplay.stackTrace}" />
           :
           : more view stuff
           :
        </h:form>
    </body>
</html>
</f:subview>

...

Code Block
titleErrorDisplay.java


import cms.beans.framework.AbstractUIBean;

import com.c2gl.jsf.framework.ApplicationResource;

import java.io.PrintWriter;
import java.io.StringWriter;

import java.util.Map;

import javax.faces.context.FacesContext;

import javax.servlet.ServletException;

public class ErrorDisplay extends AbstractUIBean {
    private static final long serialVersionUID = 3123969847287207137L;
    private static final String BEAN_NAME = ErrorDisplay.class.getName();

    public String getInfoMessage() {
        return "An unexpected processing error has occurred. Please cut and paste the following information" + " into an email and send it to <b>" +
        some email address + "</b>. If this error " + "continues to occur please contact our technical support staff at <b>" +
        some phone number etc + "</b>.";
    }

    public String getStackTrace() {
        FacesContext context = FacesContext.getCurrentInstance();
        Map requestMap = context.getExternalContext().getRequestMap();
        Throwable ex = (Throwable) requestMap.get("javax.servlet.error.exception");

        StringWriter writer = new StringWriter();
        PrintWriter pw = new PrintWriter(writer);
        fillStackTrace(ex, pw);

        return writer.toString();
    }

    private void fillStackTrace(Throwable ex, PrintWriter pw) {
        if (null == ex) {
            return;
        }

        ex.printStackTrace(pw);

        if (ex instanceof ServletException) {
            Throwable cause = ((ServletException) ex).getRootCause();

            if (null != cause) {
                pw.println("Root Cause:");
                fillStackTrace(cause, pw);
            }
        } else {
            Throwable cause = ex.getCause();

            if (null != cause) {
                pw.println("Cause:");
                fillStackTrace(cause, pw);
            }
        }
    }
}

Also have a look at our ExceptionUtils class. It encapsulates the way how to get the real root cause

Code Block
java
java

	[1] List exceptions = ExceptionUtils.getExceptions(exception);
	[2] Throwable throwable = (Throwable) exceptions.get(exceptions.size()-1);
	[3] String exceptionMessage = ExceptionUtils.getExceptionMessage(exceptions);

...

So the new fillStackTrace become

Code Block
java
java

    private void fillStackTrace(Throwable ex, PrintWriter pw)
    {
        if (null == ex) {
            return;
        }

	List exceptions = ExceptionUtils.getExceptions(exception);
	Throwable throwable = (Throwable) exceptions.get(exceptions.size()-1);

        for (int i = 0; i<exceptions.size(); i++)
        {
            if (i > 0)
            {
                pw.println("Cause:");
            }
            throwable.printStackTrace(pw);
        }
    }

...

Another way to handle this would be to use an intermediate step by specifying a non-JSF URL as the error page and then somehow redirecting to the JSF error page. For example, for the 404 error code you could specify ''/error/404_redirect.html'':

Code Block
xml
xml

<html><head><meta http-equiv="Refresh" content="0; URL=/DPSV4/error/404.jsf"></head></html>

...

Code Block
titleRedirectServlet.java

public class RedirectServlet extends HttpServlet {
	private static final String URL_PREFIX = "url=";

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String query = req.getQueryString();
		if ( query.contains(URL_PREFIX) ) {
			String url = query.replace(URL_PREFIX, "");
			if ( !url.startsWith(req.getContextPath()) ) {
				url = req.getContextPath() + url;
			}
			
       		resp.sendRedirect(url);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}

}

used like this in ''web.xml'':

Code Block
xml
xml

	<servlet>
	    <servlet-name>Redirect Servlet</servlet-name>
	    <servlet-class>ca.gc.agr.ops.web.jsf.redirect.RedirectServlet</servlet-class>
	</servlet>    
	<servlet-mapping>
	    <servlet-name>Redirect Servlet</servlet-name>
	    <url-pattern>/redirect</url-pattern>
	</servlet-mapping>

	<error-page>
		<error-code>403</error-code>
		<location>/redirect?url=/error/403.jsf</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/redirect?url=/error/404.jsf</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/redirect?url=/error/500.jsf</location>
	</error-page>

...

with this web.xml configuration

Code Block
xml
xml

	<error-page>
		<error-code>500</error-code>
		<location>/error.jsp</location>
	</error-page>

...

A fragment of your dependency plan would be like this:

Code Block
xml
xml

<dep:dependencies>
.
.
	<dep:dependency>
		<dep:groupId>org.apache.myfaces.core</dep:groupId>
		<dep:artifactId>myfaces-api</dep:artifactId>
		<dep:type>jar</dep:type>
	</dep:dependency>
	<dep:dependency>
		<dep:groupId>org.apache.myfaces.core</dep:groupId>
		<dep:artifactId>myfaces-impl</dep:artifactId>
		<dep:type>jar</dep:type>
	</dep:dependency>
.
.
</dep:dependencies>

<dep:hidden-classes>
	<dep:filter>javax.faces</dep:filter>
	<dep:filter>org.apache.myfaces</dep:filter>
</dep:hidden-classes>

This solution was tested under Apache Geronimo 2.1.3., but will probably be similar in other versions.

Wiki Markup
{scrollbar}