ice cream cone on side with ice cream spilled

Error Handling in AEM

Juan Ayala, January 27, 2022

Recently, I worked on an AEM project that included a lot of legacy code. Some of this code involved error handling and based on its git history, it pre-dated ACS Commons. For perspective, the ACS Commons Page Error Handler has been an AEM feature since version 1.0.0 in 2013! 

The client knew they needed its error handling capability to function well going forward and my task was to determine if the legacy code could be built upon or if it should be replaced with something new. 

This got me thinking about one of the oldest topics in AEM and in computer programming for that matter…error handling. As I write this it is still January 2022 so let’s start off the new year with a review!

Why customize error messaging?

A “404” or page not found error is quite common and something a site visitor will encounter eventually. The default 404 page on an AEM publish instance looks like the below:

And on the author instance, it looks like this:Screencap of text on screen "no resource found"

Again, we ask the question, why customize the error messaging?

  • Don’t give away unnecessary information. On the publish page, giving away as little as “Apache Sling” to would-be attackers could end badly. Giving away the stack trace on the author page is even worse. Granted, there are other ways to find out if a site is running AEM. And your author instance is behind a firewall. As long as none of your content authors are hackers by night, the risk isn’t great but there is a risk.
  • Error pages should be branded and friendly. If a user navigates to a page that is no longer active (or is erroring out) you still want to keep the user on your site. At minimum, the site logo and navigation should be present to keep them on the site. There is also an opportunity to guide the user to alternative pages when they hit an error. Otherwise, they may leave and never come back!
  • Content authors should be able to edit error pages. AEM is a content management system after all. Content authors should be able to update the content of an error page, in any language for any of the sites hosted on your instance or sections and areas within those sites.
  • Look like a pro! If I land on your site and I see one of the above pages, you would look like an amateur. It would make me uncomfortable. I would not be creating an account, much less giving you my credit card info. I would leave and never come back.

Compared to other development tasks, setting up error handling in AEM is trivial. It boils down to some config files and a couple of overlays. Because it is such an old topic you may find some outdated information when you search for it. Even the video for the ACS Commons Page Error Handler is a bit outdated. 

Now, I’ll walk through the modernized version of how to do this.

Required Content Pages

There are two ways to set up error handling. Regardless of which one you choose, you will need landing pages for your errors. Typically you only need one for 500 errors and one for a 404 error. And on a multi-language site, there is a set of these per language root in a folder called errors. So for example the pages below could be created to address most errors:

  • /content/mysite/us/en/errors/404.html
  • /content/mysite/us/en/errors/500.html

There is nothing special about these pages. They can be created with static or dynamic templates.

Error Handling on Apache

To do this, you need to configure the dispatcher module so it does not spool errors and instead delegates to Apache. Apache will need a map of error codes and their corresponding content paths.

First, set the DispatcherPassError module parameter to 1. This will tell the dispatcher module that if it receives a response >= 400, it should delegate it to Apache.

Second, Apache will need to know what to do to serve. Use the ErrorDocument to tell it where to fetch the error pages for each code. Assuming a multilingual content structure, place the following directives in your VHost.

SetEnvIf Request_URI "^/([a-z]{2})/([a-z]{2})/.*" ERRORDOCS=$1/$2/errors
SetEnvIfExpr "%{ENV:ERRORDOCS} =~ /^$/" ERRORDOCS=us/en/errors
ErrorDocument 500 /%{ENV:ERRORDOCS}/500.html
ErrorDocument 404 /%{ENV:ERRORDOCS}/404.html

This diagram represents the path of a 404 response:
Mapping of 404 and caches

The benefit to this setup is that the dispatcher will only fetch the error page once, and cache it. If a 404/500 error is ever encountered again, it will NOT reach back to the publisher and make it render the page again. This will guard your site against a distributed denial-of-service (DDoS) attack too.

It’s that simple; a couple of content pages, one dispatcher parameter and a few apache directives.

Error Handling in Sling

Alternatively, you may prefer the Sling method rather than the previously described Apache method.

To do this, you need to overlay the default scripts and start writing your custom code. But who has time for all that code when ACS commons already has it? In case this is your preferred method, let me describe it below.

First, make sure the DispatcherPassError module parameter is 0. Unlike the dispatcher configuration above, this ensures that the dispatcher module will spool the error and not tell Apache about it.

Second, overlay the following files just as stated in their instructions.

  • /apps/sling/servlet/errorhandler/404.jsp
    <%@page session="false"%><%
    %><%@include file="/apps/acs-commons/components/utilities/errorpagehandler/404.jsp" %>
  • /apps/sling/servlet/errorhandler/default.jsp
    <%@page session="false"%><%
    %><%@include file="/apps/acs-commons/components/utilities/errorpagehandler/default.jsp" %>

Finally, update your page properties dialog. Add a property called errorPages. The ACS error handler uses this inheritable property to determine where to get error content pages

The ACS instructions are somewhat outdated because they are still using a Classic UI example. Also, this step is very subjective. Everyone’s setup is different. Since overlaying the scripts above affect the entire instance, I chose to add it to all pages that fall under the basic page hierarchy. I overlaid the file responsible for the Advanced tab by creating the following.

  • /apps/wcm/foundation/components/basicpage/v1/basicpage/tabs/advanced/.content.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured">
    <items jcr:primaryType="nt:unstructured">
    <column
    jcr:primaryType="nt:unstructured">
    <items jcr:primaryType="nt:unstructured">
    <section1 jcr:primaryType="nt:unstructured">
    <items jcr:primaryType="nt:unstructured">
    <errorPages
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
    fieldLabel="Error Pages"
    name="./errorPages"
    nodeTypes="cq:Page"
    rootPath="/content">
    <granite:data
    jcr:primaryType="nt:unstructured"
    cq-msm-lockable="errorPages"/>
    </errorPages>
    </items>
    </section1>
    </items>
    </column>
    </items>
    </jcr:root>

The overlay + resource merger allowed me to insert that field within a specific section of an OOTB page properties tab.

Screencap of AEM Web Console showing Error Page Handler Cache

This is a diagram that represents the path of a 404 response:

Mapping of 404 and caches

  1. A user requests a page.
  2. The dispatcher doesn’t have it so it asks the publish.
  3. The publish doesn’t have it so it sends back a 404.
  4. The dispatcher will not cache error responses, so it serves it right away.

The benefit to this approach is flexibility. You can write code to do anything you need to do and ACS has done this by implementing a lot of features. For example, the author controls the source of error pages through the page property. And, there is an in-memory cache to guard against DDoS. Navigate to the JMX console to view cache entries.

Error Handling at the Component Level

ACS commons has two error handlers. The error page handler (the one above) and the component error handler. They do not depend on each other.

If for some reason a component has an error, the exception will get handled gracefully. Take for example this HTL component.

<div data-sly-use.model="com.mysite.core.models.HelloWorldModel">
<!-- More code here -->
</div>

If the model’s @PostConstruct throws an exception, there will be no visual cue to let the content author know. And then what do authors usually do? Try to drop it in a few more times.

The only way for you to see the exception on the author would be to open the Developer mode.

All you need to do is enable the handler and adjust the ranking of the WCM Developer Mode Filter.

  • com.adobe.acs.commons.wcm.impl.ComponentErrorHandlerImpl.cfg.json
    {
        "edit.enabled": true
    }    
  • com.day.cq.wcm.core.impl.WCMDeveloperModeFilter.cfg.json
    {
        "wcmdevmodefilter.enabled": true,
        "service.ranking:Integer": 75000
    }

The only reason I bring up this handler is to disambiguate between the two handlers ACS provides. In my opinion, this component error handler is not worth setting up. Good design and code analyzers should help avoid things like null pointer exceptions, the biggest cause of component failure IMHO.

Conclusion

When it comes to handling errors in Sling or Apache, my personal preference is Apache. The Sling approach is way more flexible, but, I prefer to keep things simple. Typically, a 404 and 500 page per site and per locale is enough.

Are you thinking about making some changes to your AEM implementation? 3|SHARE would love to help.  Contact us!

Hero Photo by Sarah Kilian on Unsplash

Topics: Development, Adobe, Tech, Customer Experience, Apache

Juan Ayala

Juan Ayala is a Technical Architect at 3|SHARE. He boasts three Adobe Experience Manager certifications: Dev/Ops Engineer, Sites Architect and Sites Developer. His favorite thing about being on the 3|Share is that he is able to do relevant, cutting-edge work that keeps him engaged. Outside of work, Juan enjoys DIY, motorcycling and rescuing dogs from the pound.