Expand my Community achievements bar.

Submissions are now open for the 2026 Adobe Experience Maker Awards.

URL/API Cache flush at dispatcher on Page Publishing

Avatar

Community Advisor

9/23/24


URL Cache flush at dispatcher on Page Publishing

  by Pulkit Vashistha

Overview

This is a use case in which one would need to clear cache of a URL ( e.g. API URL ) on publishing of a page from author instance to publish instance.
This solution is for AEM on prem.

 

Prerequisites

1. You need to have a flush agent configured from publish to dispatcher.
    You can configure the same at http://[host]:[port]/etc/replication/agents.publish.html

2. ACS Commons is required for implementation of above use case.

 

Solution

First of all you need to create a Sling Event Handler to work on publisher to catch publishing events.

 

 

@component(service = EventHandler.class,
        immediate = true,
        property = {
                Constants.SERVICE_DESCRIPTION + "=Listener on page replication to clear url cache",
                EventConstants.EVENT_TOPIC + "=" + ReplicationEvent.EVENT_TOPIC
        })
public class CacheInvalidateListener implements EventHandler {

    @reference
    private ResourceResolverFactory resolverFactory;

    @reference
    private JobManager jobManager;

    ResourceResolver resourceResolver;

    private final Logger logger = LoggerFactory.getLogger(CacheInvalidateListener.class);

    @Override
    public void handleEvent(final Event event) {
        try {
            logger.debug("Inside CacheInvalidateListener==============");
            // resourceResolver = fetch resource resolver using service user with necessary permissions
            if (resourceResolver == null) {
                logger.debug("ResourceResolver is null");
            }
            ReplicationEvent replicationEvent = ReplicationEvent.fromEvent(event);
            Object path = replicationEvent.getReplicationAction().getPaths();
            if(null == path) {
                return;
            }
            if (!(path instanceof String[])) {
                return;
            }
            String[] paths = (String[])path;
            for (String pagePath : paths) {
                triggerCacheFlushJob(pagePath);
            }

        } catch (Exception e){
            logger.error("Exception thrown {}",e.getMessage());
        }
    }

    private void triggerCacheFlushJob(String pagePath) {
        Resource resource = resourceResolver.getResource(pagePath);

        if (ResourceUtil.isNonExistingResource(resource)) {
            logger.debug("Resource is null for path: {}", pagePath);
            return;
        }
            Map<String, Object> jobProperties = new HashMap<>();
            jobProperties.put("pagePath", pagePath);
            jobManager.addJob("com/bin/cacheflush", jobProperties);
            logger.debug("Cache flush job triggered for pagePath: {}", pagePath);
    }
}

 

 

NOTE:
Now as you can check that we are using a Sling Job for assigning the task of page cache flush.This is because we want this task to continue in background and such intensive task are better assigned to Sling Job.We faced issues in which the listener would stop working if sent too many publishing requests.
This may be because in our case we were clearing cache for a lot of URLs at each page publication.

 

------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Now we will write Sling Job logic to flush URL cache on demand.

 

 

@component(
        immediate = true,
        service = JobConsumer.class,
        property = JobConsumer.PROPERTY_TOPICS + "=com/bin/cacheflush"
)
public class CacheFlushJobConsumer implements JobConsumer {

    private static final Logger logger = LoggerFactory.getLogger(CacheFlushJobConsumer.class);

    @reference
    private ResourceResolverFactory resolverFactory;

//We will use acs commons DispatcherFlusher API
    @reference
    private DispatcherFlusher dispatcherFlusher;

    @Override
    public JobResult process(Job job) {
        String pagePath = (String) job.getProperty("pagePath");
        ResourceResolver resourceResolver;
        //resourceResolver = fetch resource resolver using service user with necessary permissions
        try (resourceResolver) {
            if (resourceResolver == null) {
                logger.error("Unable to get resource resolver");
                return JobResult.FAILED;
            }
            // in my case I am clearing Page table of content cache
            invalidateTOCCache(pagePath, resourceResolver);
            return JobResult.OK;

        } catch (Exception e) {
            logger.error("Exception during cache flush for page {}: {}", pagePath, e.getMessage());
            return JobResult.FAILED;
        }
    }

    private void invalidateTOCCache(String parentPagePath, ResourceResolver resourceResolver) {
        try {
            Resource parentPageResource = resourceResolver.getResource(parentPagePath);
            if (parentPageResource != null) {
                // In my case clearing page toc cache
                String apiPath = parentPagePath + "/_jcr_content.toc.json";
                logger.debug("Flushing dispatcher cache for url = {}", apiPath);
                dispatcherFlusher.flush(resourceResolver, ReplicationActionType.DELETE, false, new DispatcherFlushFilter(DispatcherFlushFilter.FlushType.ResourceOnly), new String[]{apiPath});
            }
        } catch (Exception e) {
            logger.error("Error flushing dispatcher cache for path: {}", parentPagePath, e);
        }
    }
}

 

 

Above approach is the most optimized way we could implement in limited time.

Thanks and regards

 

Q&A

Please use this thread to ask questions relating to this article
Queries and suggestions are most welcome.