AEM Listener to handle AEM Cloud services async jobs | Community
Skip to main content
Level 2
November 15, 2025
Question

AEM Listener to handle AEM Cloud services async jobs

  • November 15, 2025
  • 2 replies
  • 208 views

I want to trigger listener which which further sends email whenever any async job is completed on AEM Cloud.

But whatever event I am trying is not hitting the listener

org/apache/sling/event/notification/job/FINISHED Or

com/adobe/granite/async/job/SUCCEEDED.

 

or I have the 2nd approach where I can check /var/asyncjobs where if any new node creates I can trigger listener but in that node I see no property to differentiate between jobs( like I only want for asset

import or zip unarchive).

 
can someone please help here.

 

 

2 replies

muskaanchandwani
Adobe Employee
Adobe Employee
November 16, 2025

Hello @rinkishahi 

You may also consider :
For OOTB Async Jobs, AEMaaCS already sends Inbox and Email notifications (if configured) when any built‑in asynchronous job finishes.

You can do so by setting emailEnabled to 'true' in respective OSGi Configurations


Reference :
https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/operations/asynchronous-jobs#configuring-synchronous-delete-operations

Level 2
November 17, 2025

Thanks Muskann for replying,

I know about email notification for async jobs but that only goes to admins.

And that is why I need this listener to send to a specific group.

 

And when I used org/apache/sling/event/notification/job/FINISHED, the listener triggered email but for other jobs not for my async job. It did not print the logger which was first line in the handle event method. 

@8220494(
service = EventHandler.class,
immediate = true,
property = {
EventConstants.EVENT_TOPIC + "=org/apache/sling/event/notification/job/FINISHED"
}
)
public class AssetUploadListener implements EventHandler {

private static final Logger log = LoggerFactory.getLogger(AssetUploadListener.class);
private static final String EMAIL_TEMPLATE= "/etc/notification/email/brandportal-asset-import-notification.html";
private static final String LIBRARIAN_GROUP= "asset-manager-dam-librarian";
private static final String UNKNOWN= "Unknown";

@3214626
private EmailSenderService emailSenderService;

@3214626
private ResourceResolverFactory resolverFactory;

private boolean enableListener;


@9944223
public void handleEvent(Event event) {

 

String[] propertyNames = event.getPropertyNames();
for (String key : propertyNames) {
log.info("Event property: {} = {}", key, event.getProperty(key));
}


String jobStatus = (String) event.getProperty("job.status");
String jobTitle = (String) event.getProperty("job.title");
String jobDescription = (String) event.getProperty("job.description");

log.info("Async Job Event -> Status: {}, Title: {}, Description: {}", jobStatus, jobTitle, jobDescription);

// Flexible condition for UNZIP jobs
log.info("UNZIP job completed successfully for folder: {}", jobDescription);

try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(
Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "emailService"))) {

// For testing, hardcoded email
List<String> emailIds = Collections.singletonList("rinki.shahi@aaaa.com");

if (!emailIds.isEmpty()) {
Map<String, String> emailParams = new HashMap<>();
emailParams.put("brandName", extractBrandName(jobDescription));
emailParams.put("brandFolderPath", jobDescription != null ? jobDescription : UNKNOWN);
emailParams.put("subject", "UNZIP Job Success");

emailSenderService.sendEmailWithAttachment(EMAIL_TEMPLATE, emailParams, Collections.emptyMap(), resolver, emailIds.toArray(new String[0]));
log.info("Email sent successfully to {}", emailIds);
} else {
log.warn("No email recipients found in group: {}", LIBRARIAN_GROUP);
}

} catch (Exception e) {
log.error("Failed to send UNZIP job notification email", e);
}
}

muskaanchandwani
Adobe Employee
Adobe Employee
November 19, 2025

Hello @rinkishahi, your listener does seem correct, but the OOTB asynchronous operations that appear under Tools > General > Jobs (page move, asset bulk operations, etc.) use AEM’s internal Granite async framework.

Some other alternatives you may consider :
- Implement your own Sling/Granite job for the unzip/import use case and, in its JobConsumer, look up the group and send the email yourself.
Or, for asset-related flows, use AEM Events via Adobe I/O and have an external handler send emails to the desired group.

Adobe Employee
November 17, 2025

Hi @rinkishahi 

Here is my 2 cents.

Why not listen at the overall async job level
The async job framework (com.adobe.granite.async) in AEM Cloud aggregates tasks from multiple domains — asset ingestion, unarchiving, launch copy, metadata extraction, etc.
If you hook at the global level (e.g. com/adobe/granite/async/job/SUCCEEDED or under /var/asyncjobs), you’ll only get coarse “job completed” events with no context on what triggered them — no asset ID, user, or business intent. That makes it impossible to tailor messaging because the payload and semantics differ per job type.

 

Recommended pattern
Use domain‑specific completion listeners registered at the level where the job’s outcome is meaningful. For instance:

  • Asset ingestion -> listen for com.day.cq.dam.api.event.AssetEvent or AssetCreated / AssetAdded OSGi topics.
  • ZIP unarchive -> register a listener in the importer implementation (via AsyncHandler or workflow step) that emits a custom event once the unarchive completes.
  • Custom async jobs -> emit your own EventAdmin event (e.g. "myproject/async/job/SUCCEEDED") with job metadata in properties; your mail service listens to that.