Advanced Exception Handling

Note:
Use advanced exception handling when you need precise control over exception processing and want to handle different combinations of exceptions in specific ways.

For most scenarios, you can implement exception handling and error page creation using the approaches described in Simple exception handling. However, these simpler methods have limitations that may not meet all requirements.

Limitations of Simple Exception Handling

Simple exception handling does not provide control over the entire component tree. The last HstComponent that calls setStatus determines the response. Additionally, using forward or sendRedirect interrupts processing, so any subsequent HstComponents are not executed.

Delegating error handling to individual HstComponent implementations in their doBeforeRender methods is not ideal. Instead, it is more effective to collect all HstComponentExceptions from executed components and process them in a single, customizable handler.

Runtime Exception Handling in Development and Production Modes

When development.mode=true, runtime exceptions thrown in HstComponents are propagated directly. When development.mode=false, these exceptions are wrapped in a HstComponentException and can be handled as described below.

Rather than calling forward or sendRedirect within your HstComponent, define and throw custom exceptions. These exceptions can be runtime exceptions or should extend HstComponentException. For example:

MyDocumentNotFoundException:

public class MyDocumentNotFoundException extends HstComponentException { private static final long serialVersionUID = 1L; public MyDocumentNotFoundException() { super(); } public MyDocumentNotFoundException(String msg, Throwable nested) { super(msg, nested); } public MyDocumentNotFoundException(String message) { super(message); } public MyDocumentNotFoundException(Throwable nested) { super(nested); } }

When a required bean is missing in an HstComponent, throw the exception as follows:

throw new MyDocumentNotFoundException("Did not find a document at " + path);

HstRequestProcessing will continue, and other HstComponents may also throw exceptions that extend HstComponentException or are wrapped in one. For example:

try { HstQuery q = this.queryManager.createQuery(n); q.execute(); } catch (QueryException e) { throw new HstComponentException("Query exception", e); }

After all doBeforeRender(...) (and similarly doAction, which only applies to a single component) methods have executed, multiple HstComponents may have thrown exceptions.

The PageErrorHandler

Implement a custom PageErrorHandler to control how exceptions are processed. The handler determines the response based on the set of exceptions collected during request processing.

Implement the following method:

Status handleComponentExceptions(PageErrors pageErrors, HstRequest hstRequest, HstResponse hstResponse);

The Status enum is defined as:

enum Status { HANDLED_TO_STOP, NOT_HANDLED, HANDLED_BUT_CONTINUE, }

See the example at the end of this page for a sample PageErrorHandler implementation.

Bloomreach Content provides a DefaultPageErrorHandler that logs all exceptions at the warning level. To implement custom exception handling, you can override this default handler using one of the following methods:

  1. Override the DefaultPageErrorHandler in the Spring configuration.
  2. Set a custom PageErrorHandler as an attribute on the hstRequest. Use the attribute name ContainerConstants.CUSTOM_ERROR_HANDLER_PARAM_NAME.
  3. Configure the root HstComponent with the parameter name org.hippoecm.hst.core.container.custom.errorhandler and set the parameter value to the fully qualified class name of your PageErrorHandler implementation.

1. Override the DefaultPageErrorHandler in the Spring Configuration

By default, the HST container uses a global page error handler. You can override this handler by defining a custom bean in an assembly XML file under /META-INF/hst-assembly/overrides. For example:

<bean id="org.hippoecm.hst.core.container.PageErrorHandler" class="org.hippoecm.hst.core.container.DefaultPageErrorHandler"> </bean>

Override the bean with the same ID to replace the default handler globally.

Ensure that your hst-config.properties file includes the following configuration:

assembly.overrides = META-INF/hst-assembly/overrides/*.xml

2. Set a Custom PageErrorHandler as an Attribute on the hstRequest

During the execution of any HstComponent, you can assign a custom PageErrorHandler to the hstRequest. If exceptions occur during processing, the request processing will invoke this handler instead of the global one.
Note: Setting the PageErrorHandler at runtime is possible but not recommended for most use cases.

3. Configure the PageErrorHandler on the Root HST Component

You can specify a different error handler for each root HST component. For example, configure the hst:component node as follows:

/detailpage: jcr:primaryType: hst:component hst:referencecomponent: hst:pages/standard hst:parameternames: [org.hippoecm.hst.core.container.custom.errorhandler, notfound, unauthorized] hst:parametervalues: [org.hippoecm.hst.demo.util.SimplePageErrorHandler, /error/404, /error/401]

Note:
From HST version 2.10.01 and higher, use the hst:page_errorhandlerclassname property instead of hst:parameternames and hst:parametervalues. The previous configuration is still supported, but the following approach is preferred:

/detailpage: jcr:primaryType: hst:component hst:referencecomponent: hst:pages/standard hst:page_errorhandlerclassname: org.hippoecm.hst.demo.util.SimplePageErrorHandler hst:parameternames: [notfound, unauthorized] hst:parametervalues: [/error/404, /error/401]

The parameter values are used for forwarding or redirecting. Ensure that your sitemap includes error matchers as described in Simple exception handling.

Example: Custom PageErrorHandler with Custom Exceptions

When implementing a custom error handler, check for specific exceptions and forward to the appropriate error page.
Tip:
Exceptions thrown during doBeforeRender() or doAction() are wrapped in a HstComponentException by the HST. The stack trace of the wrapper exception may not be useful; use getCause() to access the underlying exception. To avoid excessive logging, log only the cause where appropriate. Some HstComponentException instances are thrown by the HST itself, so check for a non-null cause.

Example implementation:

public class CustomPageErrorHandler implements PageErrorHandler { protected final static Logger log = LoggerFactory.getLogger( CustomPageErrorHandler.class); public Status handleComponentExceptions(PageErrors pageErrors, HstRequest hstRequest, HstResponse hstResponse) { logWarningsForEachComponentExceptions(pageErrors); ResolvedSiteMapItem resolvedSiteMapItem = hstRequest.getRequestContext().getResolvedSiteMapItem(); for (HstComponentInfo componentInfo : pageErrors.getComponentInfos()) { for (HstComponentException componentException : pageErrors.getComponentExceptions(componentInfo)) { String errorPage = null; if(componentException instanceof MyUnauthorizedException || componentException.getCause() instanceof MyUnauthorizedException) { errorPage = resolvedSiteMapItem .getHstComponentConfiguration() .getParameter("unauthorized"); } if(componentException instanceof DocumentNotFoundException || componentException.getCause() instanceof DocumentNotFoundException) { errorPage = resolvedSiteMapItem .getHstComponentConfiguration() .getParameter("notfound"); } try { if(errorPage != null) { hstResponse.forward(errorPage); return Status.HANDLED_TO_STOP; } } catch (IOException e) { log.warn("Failed to forward page: {}. {}", errorPage, e.toString()); } } } return Status.HANDLED_BUT_CONTINUE; } protected void logWarningsForEachComponentExceptions( PageErrors pageErrors) { for (HstComponentInfo componentInfo : pageErrors.getComponentInfos()) { for (HstComponentException componentException : pageErrors.getComponentExceptions(componentInfo)) { if (log.isDebugEnabled()) { log.debug("Component exception found on " + componentInfo.getComponentClassName(), componentException); } else if (log.isWarnEnabled()) { log.warn("Component exception found on {}: {}", componentInfo.getComponentClassName(), componentException.toString()); } } } }
Share Feedback
Page: /build/error-handling/4.-advanced-exception-handling
Section: Build
Category *
Advanced Exception Handling | Bloomreach Content Documentation