AEM Cache Invalidation Using Sling Content Distribution

This tutorial will help us to clear servlet cache on publish of page or resource. We are going to use sling content distribution API to clear cache.

It is not a best practice to use custom code for invalidate cache explicitly. There are two custom ways we can clear cache using below API’s:

  1. Sling Content Distribution API (preferred)
  2. Replication API to invoke the publish Dispatcher flush replication agent.

Sling content distribution module facilitates the distribution of Sling resources among various Sling instances. Operating at the path level, the API utilizes distribution agents to enable the transfer of specific paths between instances.

Sling content Distribution supports ADD, DELETE and INVALIDATE actions. Within the framework of Sling Content Distribution, cache invalidation is possible upon content publishing, removal, and even without triggering a publication event.

On Author mode, use EventHandler interface topic as com/day/cq/replication to invalidate cache on page publish/unpublish.

Sling Content Distribution(SCD)

Note: Test above code in AEM as a cloud service environment not in local. This will not work in local.

package com.practice.core.utils;


import com.day.cq.replication.*;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.practice.core.constants.CommonConstants;
import com.practice.core.services.PracticeUserService;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.distribution.*;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;

@Component(immediate = true, service = EventHandler.class, property = {
        EventConstants.EVENT_TOPIC+"="+ReplicationAction.EVENT_TOPIC
})
public class InvalidateCacheEventHandler implements EventHandler {

    // PracticeUserService will help us to fetch system user
    @Reference
    PracticeUserService practiceUserService;

    // Distributor will help us to invalidate cache
    @Reference
    private Distributor distributor;

    private static final Logger LOG = LoggerFactory.getLogger(InvalidateCacheEventHandler.class);

    // Servlet path going to invalidate 
    private static final String servletPath  =  "/content/practice/ea/en/data/product.products.json";

    public void handleEvent(Event event) {

        ReplicationAction replicationAction = ReplicationAction.fromEvent(event);
        ReplicationActionType replicationType = replicationAction.getType();

        String agent = getAgentFromReplicationEvent(event);

        LOG.info("Agent from event : {}", agent);
        
        // Flush or invalidate cache if replication type is activate
        if (replicationType.equals(ReplicationActionType.ACTIVATE)) {

            try (ResourceResolver flushingResourceResolver = practiceUserService.getServiceResolver(
                CommonConstants.PRACTICE_READ_CONTENT_ETC_GENERIC_LIST)) {                
                
                // Invalidate cache
                invalidateCache(Arrays.asList(servletPath), agent,
                  DistributionRequestType.INVALIDATE, Boolean.TRUE, flushingResourceResolver);
            } catch (Exception e) {
                LOG.error("Replication exception occurred during Dispatcher Flush request.", e);
            }
        }
    }

    // Function will help us to get agent
    private String getAgentFromReplicationEvent(Event event) {
        if (Objects.nonNull(event)) {
            List agentIds = (List) event.getProperty("agentIds");
            String agent = (String) agentIds.get(0);
            return Objects.nonNull(agent) ? agent : "publish";
        }
        return "publish";
    }

    private void invalidateCache(List<String> pathsToInvalidate, String agent,
                                 DistributionRequestType requestType, Boolean isDeep, ResourceResolver resolver) {
        // Invalidate cache for specific paths
        DistributionRequest distributionRequest = new SimpleDistributionRequest(requestType,
                isDeep, pathsToInvalidate.toArray(new String[0]));
        if (!pathsToInvalidate.isEmpty()) {
            
            // Invalidate cache using DistributionRequest
            DistributionResponse distributionResponse = distributor.distribute(agent,
                    resolver, distributionRequest);
            LOG.debug("Distribution Response: {}", distributionResponse);
            LOG.debug("Distribution message: {}", distributionResponse.getMessage());
        }
    }
}

Allow JSON extension as part of /invalidate section under /cache as part of dispatcher configuration. It will allow us to clear cache for servlet having JSON as extension.

/0003 {
    /glob "*.json"
    /type "allow"
}

Replication API

Replication API also allow us to clear cache on activation, deactivation and deletion of page.

The flush agent endpoint is not configurable but rather preconfigured to point to Dispatcher, matched with the publish service running alongside the flush agent.

The flush agent can typically be triggered by custom code based on OSGi events or workflows.

String[] paths = …
ReplicationOptions options = new ReplicationOptions();
options.setSynchronous(true);
options.setFilter( new AgentFilter {
  public boolean isIncluded (Agent agent) {
   return agent.getId().equals("flush");
  }
});

Replicator.replicate (session,ReplicationActionType.DELETE,paths, options);

For Replication API also, allow JSON extension as part of /invalidate section under /cache as part of dispatcher configuration. It will allow us to clear cache for servlet having JSON as extension.

/0003 {
    /glob "*.json"
    /type "allow"
}

Imran Khan

Specialist Master (Architect) with a passion for cutting-edge technologies like AEM (Adobe Experience Manager) and a proven track record of delivering high-quality software solutions.

  • Languages: Java, Python
  • Frameworks: J2EE, Spring, Struts 2.0, Hibernate
  • Web Technologies: React, HTML, CSS
  • Analytics: Adobe Analytics
  • Tools & Technologies: IntelliJ, JIRA

🌐 LinkedIn

📝 Blogs

📧 Imran Khan