AEM Event Handler to Purge CDN Cache


As part of this blog we will try to clear cache with the help of Event Handler on page activation, deactivation and delete. There are various other custom approaches we can follow to purge CDN cache.

Before reading this blog please visit this link to get overall theoretical knowledge around dispatcher, CDN, SSL and how they combinedly works and also this link to see what is required to purge CDN cache using code.

Follow this link if we wish to completely wipe out cache or purge everything using workflow.

Follow below following steps to purge cache for specific page using Event Handler:

Note: Please read comments while going through code will help you to have more understanding around it.

  1. Create custom service which will allow us to clear cache using path:
package com.javadoubts.core.services;

import org.osgi.service.event.Event;

public interface CloudflareCacheClear {

 /**
  * This will allow us to clear cache using file path
  * based on activate, deactivate and delete event.
  **/
 public void purgeCdnCacheByFile(Event event);

}

2.Implement purgeCdnCacheByFile service java class to purge cache using file path as shown below:

package com.javadoubts.core.services.impl;

import com.day.cq.commons.Externalizer;
import com.javadoubts.core.services.CloudflareCacheClear;
import com.javadoubts.core.services.PracticeService;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.settings.SlingSettingsService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

@Component(service = CloudflareCacheClear.class, immediate = true)
public class CloudflareCacheClearImpl implements CloudflareCacheClear {

 private static final Logger LOGGER = LoggerFactory.getLogger(CloudflareCacheClearImpl.class);

 private static final String PATHS = "paths";

 @Reference
 private Externalizer externalizer;

 @Reference
 private SlingSettingsService slingSettingsService;

 @Reference
 private PracticeService practiceService;

 /**
  * This will allow us to clear cache by path.
  **/
 public void purgeCdnCacheByFile(Event event) {
  ResourceResolver resolver = practiceService.getResourceResolver();
  if (event.containsProperty(PATHS) && null != externalizer) {
   String[] paths = (String[]) event.getProperty(PATHS);
   for (String path : paths) {
    clearCache(resolver, path);
   }
  }
 }

 /**
  * Create request body having path appended externalizer.
  **/
 private void clearCache(ResourceResolver resolver, String path) {
  // Clear the cache if current page path is starting from /content/practice
  if (StringUtils.contains(path, "/content/practice")) {

   // Find the resolver mapped shortened URL
   String mappedRes = resolver.map(path);

   if (null != mappedRes) {
    try {
     // Create a required JSON object by Cloudflare
     JSONObject jsonObject = new JSONObject();
     JSONArray jsonArray = new JSONArray();

     // Externlize the page URL  
     jsonArray.put(externalizer.publishLink(resolver, mappedRes));
     jsonObject.put("files", jsonArray);

     getConnection(jsonObject.toString());
    } catch (JSONException e) {
     LOGGER.error("JSONException {}", e);
    }
   }
  }
 }

 /**
  * Create connection using API url and authorization 
  * coming from cloud configuration.
  **/
 private static void getConnection(String body) {
  HttpURLConnection connection = null;

  /*
    Below apiUrl and authorization bearer token can be
    configured as part of system console environment secret and variable.
   */
  String apiUrl = "https://api.cloudflare.com/client/v4/zones/asaasasas87d89s97add7a97ds9a79ad/purge_cache";
  String authorization = "s90a898da8da9d9d9a9d7a97da89aad";

  if (StringUtils.isNotBlank(apiUrl) && StringUtils.isNotBlank(authorization)) {
   try {
    if (StringUtils.isNotEmpty(body)) {
     URL url = new URL(apiUrl);

     if (null != url) {
      connection = (HttpURLConnection) url.openConnection();
      clearCacheConnection(connection, apiUrl, authorization, body);
     }
    }

   } catch (IOException e) {
    LOGGER.error("CloudflareCacheClearImpl IOException {} ", e);
   } finally {
    if (null != connection) {
     connection.disconnect();
    }
   }
  }
 }

 /**
  * Make an API call to clear cache.
  **/
 private static void clearCacheConnection(HttpURLConnection connection,
  String apiUrl, String authorization, String body) throws IOException {
  if (null != connection) {

   LOGGER.error("CloudflareCacheClearImpl inside clearCacheConnection clearCache >>>>>>>>>>");

   // Set the request method to POST
   connection.setRequestMethod(HttpConstants.METHOD_POST);

   // Set headers
   connection.setRequestProperty("Content-Type", "application/json");
   connection.setRequestProperty("Authorization", "Bearer " + authorization);

   // Enable input/output streams
   connection.setDoOutput(true);

   String encodeUrl = encodeURL(apiUrl);

   if (StringUtils.isNotEmpty(encodeUrl)) {
    // Write the payload to the connection's output stream
    connection.getOutputStream().write(body.getBytes("UTF-8"));

    // Get the response code
    int responseCode = connection.getResponseCode();
    LOGGER.error("Response Code >>>>>> {}", responseCode);

    // Read the response
    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String line;
    StringBuilder response = new StringBuilder();

    while ((line = reader.readLine()) != null) {
     response.append(line);
    }

    reader.close();

    // Print the response
    LOGGER.error("Response >>>>>>>>>> {}", response);
   }
  }
 }

 private static String encodeURL(String url) throws UnsupportedEncodingException {
  return URLEncoder.encode(url, "UTF-8");
 }
}

3. Create below event handler to catch an event related to page activation, deactivation or delete which will allow us to purge cache.

package com.javadoubts.core.handler;

import com.day.cq.replication.ReplicationAction;
import com.javadoubts.core.services.CloudflareCacheClear;
import org.apache.commons.lang3.StringUtils;
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.List;

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

 @Reference
 CloudflareCacheClear cloudflareCacheClear;
 
 private static final Logger LOGGER = LoggerFactory.getLogger(PurgeCacheEventHandler.class);

 // main logic to handle specific AEM events for iPaas integration
 public void handleEvent(Event event) {
  LOGGER.debug("handleEvent : starts...");

  String eventTopic = (String) event.getProperty(EventConstants.EVENT_TOPIC);
  List<String> agents =(List<String>) event.getProperty("agentIds");
  
  // return if event topic is blank or agent type is preview.
  if(StringUtils.isBlank(eventTopic) || agents.contains("preview")){
   return;
  }
  
  // if one of the 'supported' replication events has triggered
  // logic to get the page path for replication events
  ReplicationAction actionType = ReplicationAction.fromEvent(event);
  if (null != actionType) {
   cloudflareCacheClear.purgeCdnCacheByFile(event);
  }
 }
}

4. Create below externalizer configuration to fetch the prod domain in publish:

/ui.config/src/main/content/jcr_root/apps/practice/osgiconfig/config.publish.prod

{
 "externalizer.domains": [
  "publish www.javadoubts.com"
 ]
}

5. Go to any page and perform one of the page activation, deactivation or delete operation to purge cache.

6. Check error.log file to verify the Cloudflare response:

Response >>>>>>>>>> {“success”:true,”errors”:[],”messages”:[],”result”:{“id”:”aads9ada987d98a79a9d7aa97da9asad”}}

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