Expand my Community achievements bar.

AEM Listener to handle AEM Cloud services async jobs

Avatar

Level 2

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.

 

 

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

3 Replies

Avatar

Employee

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/async...

Avatar

Level 2

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. 

@component(
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";

@reference
private EmailSenderService emailSenderService;

@reference
private ResourceResolverFactory resolverFactory;

private boolean enableListener;


@Override
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);
}
}

Avatar

Employee

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.