All AEM as a Cloud Service skills: component development, Workflow model design, development, triggering, launchers, debugging, and triaging, Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening, Rapid Development Environment (RDE) deployment, log inspection, snapshots, and troubleshooting via `aio aem rde`, Jackrabbit Oak query/index tuning (property, Lucene index definitions), and extracting/inventorying JCR queries (XPath, SQL2, QueryBuilder) across a codebase.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Programmatic content publishing and distribution monitoring for Adobe Experience Manager as a Cloud Service.
These skills cover the official APIs for content replication and distribution event handling in AEM Cloud Service:
com.day.cq.replication) - Programmatic content publishingorg.apache.sling.distribution.event) - Distribution lifecycle monitoringreplication/SKILL.md)Use the official AEM Replication API to programmatically publish and unpublish content.
What it covers:
Replicator service usage with code examplesReplicationOptions for advanced configurationWhen to use:
Official API: com.day.cq.replication.Replicator
sling-distribution/SKILL.md)Monitor and react to content distribution lifecycle events.
What it covers:
When to use:
Official API: org.apache.sling.distribution.event
┌─────────────────────────────────────────────────────────┐
│ Your Code: Replication API │
│ replicator.replicate(session, ACTIVATE, "/content/...") │
└──────────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Sling Distribution: Underlying Transport │
│ │
│ [AGENT_PACKAGE_CREATED] ← Event fires │
│ ↓ │
│ [AGENT_PACKAGE_QUEUED] ← Event fires │
│ ↓ │
│ [AGENT_PACKAGE_DISTRIBUTED] ← Event fires │
│ ↓ │
│ Adobe Developer Pipeline Service │
│ ↓ │
│ [IMPORTER_PACKAGE_IMPORTED] ← Event fires on target │
└─────────────────────────────────────────────────────────┘
↓
Content is live on Publish/PreviewScenario: Publish a page and warm the CDN cache after distribution completes.
Step 1: Use Replication API to publish:
@Reference
private Replicator replicator;
// Publish content
replicator.replicate(session, ReplicationActionType.ACTIVATE, "/content/mysite/en/page");Step 2: Listen for distribution completion:
@Component(
service = EventHandler.class,
property = {
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.IMPORTER_PACKAGE_IMPORTED
}
)
public class CacheWarmingHandler implements EventHandler {
@Override
public void handleEvent(Event event) {
String[] paths = (String[]) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PATHS
);
for (String path : paths) {
warmCache(path); // Custom cache warming logic
}
}
}| Aspect | AEM 6.x | AEM Cloud Service |
|---|---|---|
| Replication API | com.day.cq.replication.Replicator | ✅ Same API available |
| Transport | Direct JCR replication | Sling Distribution via Adobe Developer pipeline |
| Agents | Manual configuration | Automatic (managed by Adobe) |
| Preview Tier | Not available | Available with agent filtering |
| Distribution Events | Limited | Full lifecycle events via org.apache.sling.distribution.event |
| Constraint | Limit |
|---|---|
| Paths per API call (recommended) | 100 |
| Paths per API call (above 100) | System auto-splits into non-transactional chunks |
| Transactional guarantee | ≤100 paths only |
| Payload size | 10 MB maximum |
ReplicationException// Listen for content fragment changes
@Component(service = EventHandler.class, property = {
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
SlingConstants.TOPIC_RESOURCE_CHANGED
})
public class AutoPublishHandler implements EventHandler {
@Reference
private Replicator replicator;
@Reference
private ResourceResolverFactory resolverFactory;
@Override
public void handleEvent(Event event) {
String path = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
if (isContentFragment(path)) {
try (ResourceResolver resolver = getServiceResolver()) {
Session session = resolver.adaptTo(Session.class);
replicator.replicate(session, ReplicationActionType.ACTIVATE, path);
} catch (Exception e) {
LOG.error("Auto-publish failed", e);
}
}
}
private ResourceResolver getServiceResolver() throws LoginException {
Map<String, Object> param = Map.of(
ResourceResolverFactory.SUBSERVICE, "contentPublisher"
);
return resolverFactory.getServiceResourceResolver(param);
}
private boolean isContentFragment(String path) {
return path != null && path.startsWith("/content/dam") &&
path.contains("/jcr:content");
}
}// Monitor for dropped packages
@Component(service = EventHandler.class, property = {
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.AGENT_PACKAGE_DROPPED
})
public class FailureAlertHandler implements EventHandler {
@Override
public void handleEvent(Event event) {
String packageId = (String) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PACKAGE_ID
);
String[] paths = (String[]) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PATHS
);
LOG.error("Distribution failed: packageId={}, paths={}",
packageId, String.join(",", paths));
// Send alert to operations team
alertService.sendAlert("Distribution failure", packageId);
}
}// Warm cache after successful distribution
@Component(service = EventHandler.class, property = {
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.IMPORTER_PACKAGE_IMPORTED
})
public class CacheWarmingHandler implements EventHandler {
@Override
public void handleEvent(Event event) {
String[] paths = (String[]) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PATHS
);
for (String path : paths) {
String url = convertToPublicUrl(path);
httpClient.execute(new HttpGet(url)); // Warm cache
}
}
}// Workflow step: Publish to Preview for review
@Component(service = WorkflowProcess.class, property = {
"process.label=Publish to Preview for Review"
})
public class PublishToPreviewStep implements WorkflowProcess {
@Reference
private Replicator replicator;
@Override
public void execute(WorkItem workItem, WorkflowSession workflowSession,
MetaDataMap args) throws WorkflowException {
String payloadPath = workItem.getWorkflowData().getPayload().toString();
Session session = workflowSession.adaptTo(Session.class);
// Create options to target Preview tier
ReplicationOptions options = new ReplicationOptions();
options.setFilter(agent -> "preview".equals(agent.getId()));
try {
replicator.replicate(
session,
ReplicationActionType.ACTIVATE,
payloadPath,
options
);
} catch (ReplicationException e) {
throw new WorkflowException("Failed to publish to Preview", e);
}
}
}// Inject the service
@Reference
private Replicator replicator;
// Single path
replicator.replicate(session, ReplicationActionType.ACTIVATE, "/content/page");
// Multiple paths
replicator.replicate(session, ReplicationActionType.ACTIVATE,
new String[]{"/content/page1", "/content/page2"}, null);
// With options
ReplicationOptions options = new ReplicationOptions();
options.setFilter(agent -> "preview".equals(agent.getId()));
replicator.replicate(session, ReplicationActionType.ACTIVATE, "/content/page", options);
// Check status
ReplicationStatus status = replicator.getReplicationStatus(session, "/content/page");
boolean isPublished = status != null && status.isActivated();// Listen for events
@Component(service = EventHandler.class, property = {
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.AGENT_PACKAGE_CREATED,
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.AGENT_PACKAGE_QUEUED,
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.AGENT_PACKAGE_DISTRIBUTED,
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.AGENT_PACKAGE_DROPPED,
org.osgi.service.event.EventConstants.EVENT_TOPIC + "=" +
DistributionEventTopics.IMPORTER_PACKAGE_IMPORTED
})
public class MyDistributionHandler implements EventHandler {
@Override
public void handleEvent(Event event) {
String packageId = (String) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PACKAGE_ID
);
String[] paths = (String[]) event.getProperty(
DistributionEventProperties.DISTRIBUTION_PATHS
);
// Handle event
}
}| Issue | Cause | Solution |
|---|---|---|
| ReplicationException | Permission denied | Check service user has crx:replicate permission |
| Content not appearing | Wrong tier selected | Verify agent filter targets correct tier (preview vs publish) |
| Event handler not firing | Wrong event topic | Use exact constants from DistributionEventTopics |
| "Too many paths" error | >100 paths lose transactional guarantee | Use ≤100 for atomicity; system auto-splits larger sets |
Replication fails:
ReplicationExceptioncrx:replicate permission on content path/system/console/slingjobsEvents not firing:
/system/console/componentsContent not on target tier:
replicator.getReplicationStatus().tessl-plugin
skills
aem-rde
aem-workflow
code-assessment
content-distribution
create-component
assets
references
dispatcher
incident-response
references
dispatcher-foundation
performance-tuning
references
dispatcher-foundation
security-hardening
references
dispatcher-foundation
technical-advisory
references
dispatcher-foundation
workflow-orchestrator
references
dispatcher-foundation
ensure-agents-md
references
migration
references
dispatcher
legacy-ui
template-modernization
validation
scripts
dispatcher-tool
validate-migration