Hi Team,
Can anyone please suggest a solution for the following scenario?
I need to flush the Dispatcher cache and regenerate it across all Dispatcher instances for a specific AEM page, without publishing or activating the page.
I'm looking for a solution that can be triggered directly from AEM, preferably using a scheduler that runs at a configurable interval.
Any guidance, sample code, or best practices to achieve this would be greatly appreciated.
Thanks in Advance.
Veera
Views
Replies
Total Likes
hi @veerareddyc1015, you can force the behaviour into the dispatcher with a POST call
POST /dispatcher/invalidate.cache HTTP/1.1
CQ-Action: Activate
`Content-Type: text/plain
CQ-Handle: path-pattern
Content-Length: numchars in bodypage_path0
page_path1
...
page_pathn
as explained here.You can set up a scheduler for each publishing instance to trigger a POST call, ensuring that all dispatchers in the infrastructure will rebuild the cache. The scheduler must compute all the links for the website, similar to a sitemap, so that these links can be included in the body of the POST request.
Thank you for the response. I'm using AEM 6.5.22.0 and have configured it as shown below. The cached page is getting removed as expected upon activation, but it's not being re-cached. Could you please let me know if I'm missing something?
Views
Replies
Total Likes
Adjusting the replication agent is ineffective because it is essential for the post to function correctly. Specifically, the header "CQ-Handle" must include the deepest page (you can refer to this documentation for clarification) among those to be invalidated. If this condition is not met, the resource will not be re-cached.
Here is the code that works correctly:
CloseableHttpResponse invalidateAndRecache(String dispatcherUrl,
Set<String> pagesToInvalidate,
String domain,
String cqHandlePath) throws IOException {
HttpPost post = new HttpPost(dispatcherUrl);
post.addHeader("CQ-Action", "Activate");
post.addHeader("CQ-Handle", cqHandlePath);
post.addHeader("Host", domain);
post.addHeader("CQ-Action-Scope", "ResourceOnly");
post.addHeader("Content-Type", "text/plain");
String bodyString = StringUtils.join(pagesToInvalidate, "\n");
StringEntity body = new StringEntity(bodyString);
post.setEntity(body);
CloseableHttpResponse execute = httpClientService.getConfiguredHttpClient().execute(post);
return execute;
}
@Component(service = HttpClientService.class, immediate = true)
public class HttpClientServiceImpl implements HttpClientService {
private CloseableHttpClient httpClient;
@Override
public CloseableHttpClient getConfiguredHttpClient() {
[...]
return this.httpClient;
}
}
if you are looking for auto flush based on scheduled frequencies, OSGI config can be helpful
1. Create OSGI config that gets reflected in Felix Server(/system/console) --> Configuration tab
DispatcherCacheRefresherConfig.java
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.AttributeDefinition;
@ObjectClassDefinition(name = "Dispatcher Cache Refresher Config")
public @interface DispatcherCacheRefresherConfig {
@AttributeDefinition(name = "Page Path", description = "Path of the page to flush and regenerate")
String pagePath();
@AttributeDefinition(name = "Dispatcher Flush URL", description = "URL used to flush the dispatcher cache")
String flushUrl();
@AttributeDefinition(name = "Cron Expression", description = "Cron expression for scheduler")
String schedulerExpression() default "0 0/30 * * * ?"; // every 30 min
@AttributeDefinition(name = "Enabled", description = "Enable or disable the job")
boolean enabled() default true;
}
2. Create a Scheduler to execute job based on the config parameters defined in above OSGI Conf
DispatcherCacheRefresher.java
import org.osgi.service.component.annotations.*;
import org.osgi.service.metatype.annotations.Designate;
import org.apache.sling.commons.scheduler.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.HttpURLConnection;
import java.net.URL;
@component(service = Runnable.class, immediate = true)
@Designate(ocd = DispatcherCacheRefresherConfig.class)
public class DispatcherCacheRefresher implements Runnable {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@reference
private Scheduler scheduler;
private DispatcherCacheRefresherConfig config;
private static final String osgiJob = "dispatcher-cache-refresher";
@activate
@MODIFIED
protected void activate(DispatcherCacheRefresherConfig config) {
this.config = config;
if (config.enabled()) {
try {
scheduler.remove(osgiJob);
scheduler.addJob(osgiJob, this, null, config.schedulerExpression(), true);
log.info("Dispatcher Cache Refresher job scheduled.");
} catch (Exception e) {
log.error("Error scheduling job", e);
}
}
}
@deactivate
protected void deactivate() {
scheduler.remove(osgiJob);
}
@Override
public void run() {
try {
flushDispatcherCache(config.flushUrl());
regeneratePage(config.pagePath());
} catch (Exception e) {
log.error("Error in dispatcher cache refresher job", e);
}
}
private void flushDispatcherCache(String flushUrl) throws Exception {
log.info("Flushing Dispatcher Cache at {}", flushUrl);
HttpURLConnection con = (HttpURLConnection) new URL(flushUrl).openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
log.info("Flush response code: {}", responseCode);
}
private void regeneratePage(String pagePath) throws Exception {
String url = "http://localhost:4503" + pagePath + ".html"; // assuming publish or internal dispatcher
log.info("Regenerating page by requesting: {}", url);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
log.info("Page regeneration response code: {}", responseCode);
}
}
Now,
Deploy the OSGi service.
Configure the values in system/console/configmgr
Just checking in — were you able to resolve your issue?
We’d love to hear how things worked out. If the suggestions above helped, marking a response as correct can guide others with similar questions. And if you found another solution, feel free to share it — your insights could really benefit the community. Thanks again for being part of the conversation!
Views
Replies
Total Likes
Views
Likes
Replies
Views
Likes
Replies