URL Cache flush at dispatcher on Page Publishing
by Pulkit VashisthaOverview
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.