/resource-change-listener
AEM Cloud Service expert skill for Sling ResourceChangeListener. Covers the lightweight listener + JobConsumer pattern, migration from javax.jcr.observation.EventListener and resource-topic OSGi EventHandler, ResourceChangeListener vs ExternalResourceChangeListener decision,
$ npx -y skills add adobe/skills --skill resource-change-listener --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/resource-change-listener
Context preview
The summary Claude sees to decide when to auto-load this skill.
AEM Cloud Service expert skill for Sling ResourceChangeListener. Covers the lightweight listener + JobConsumer pattern, migration from javax.jcr.observation.EventListener and resource-topic OSGi EventHandler, ResourceChangeListener vs ExternalResourceChangeListener decision,
SKILL.md
resource-change-listener.SKILL.mdname: resource-change-listener
description: AEM Cloud Service expert skill for Sling ResourceChangeListener. Covers the lightweight listener + JobConsumer pattern, migration from javax.jcr.observation.EventListener and resource-topic OSGi EventHandler, ResourceChangeListener vs ExternalResourceChangeListener decision, OSGi filter configuration, review checklist, troubleshooting (silent failures, hot-path blocking, missing service-user mapping), and common pitfalls.
license: Apache-2.0
Resource Change Listener — AEM as a Cloud Service
Overview
`org.apache.sling.api.resource.observation.ResourceChangeListener` is the preferred API on AEM CS for reacting to repository content changes. The listener runs on a shared Sling thread and should return as quickly as possible.
For any blocking, repository-intensive, network, workflow, replication, indexing, asset-processing, or otherwise non-trivial work, enqueue a Sling Job and perform the processing in a `JobConsumer`.
Very small in-memory operations (metrics, counters, simple event translation, lightweight filtering, or enqueueing a job) may remain inside `onChange()` provided they do not perform repository access, network I/O, workflow operations, or expensive computation.
Two interface variants:
| Interface | Receives | Use when | |-----------|----------|----------| | `ResourceChangeListener` | Local changes only (same JVM) | Post-processing what *this* pod just wrote | | `ExternalResourceChangeListener` (extends RCL) | Local **and** external (other cluster nodes) | Cluster-wide reactions: cache invalidation, replication follow-ups |
Three CS-specific constraints every listener must satisfy:
| Constraint | Why | |-----------|-----| | No `ResourceResolver` / `Session` / JCR ops inside `onChange()` | Blocks the shared listener thread; delays every other registered listener | | `getServiceResourceResolver(SUBSERVICE)` in the consumer | `getAdministrativeResourceResolver` is removed from the CS SDK | | Filter via `PATHS` / `CHANGES` OSGi properties — not in code | Sling delivers only matching events; in-code filtering wastes the listener thread |
> **`PATHS` are chosen by the implementer from the business scope.** Do not ask the customer for raw `PATHS` syntax (`/content/dam`, `glob:/**/jcr:content/*`, etc.) unless the business scope itself is unclear. Derive the path from what the listener is supposed to react to.
---
Classification — choose before making any changes
**Already implements `ResourceChangeListener`** and `onChange()` only enqueues jobs → Already compliant — verify against the [Review Checklist](#review-checklist) only.
**Already implements `ResourceChangeListener`** and `onChange()` contains business logic (resolver, JCR ops, heavy processing) → Apply **R1–R5** (skip R0).
**Implements `javax.jcr.observation.EventListener`** or **`EventHandler`** subscribed to `org/apache/sling/api/resource/Resource/*` → Apply **R0 then R1–R5**.
**Implements `EventHandler`** subscribed to replication / workflow / custom topics → Use the `event-migration` skill instead — not this one.
**One pattern per session.** If the bundle has multiple legacy listeners, migrate one class at a time.
**Before starting:** Read [`../references/aem-cloud-service-pattern-prerequisites.md`](../references/aem-cloud-service-pattern-prerequisites.md) and apply SCR→DS, service-user, and SLF4J fixes if present in the same changeset.
---
Discovery
Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook:
bash ../scripts/analyze.sh <workspace-root> --pattern resource-change-listener
**Match criteria (what the detector flags):** a class that **`implements org.apache.sling.api.resource.observation.ResourceChangeListener`** or **`ExternalResourceChangeListener`** (import-aware) — the modern API, flagged for review against the lightweight + JobConsumer contract.
Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; reached-via-base-class is not resolved. Legacy `javax.jcr.observation.EventListener` is detected by the `event-migration` pattern (matching the BPA subtype taxonomy); when its logic is plain content observation, that guide routes it back here. A class implementing both `ResourceChangeListener` and an event interface is flagged by both patterns (rare).
Resolution contract
**guided** — `apply (guided)`. The analyzer locates each `ResourceChangeListener`; remediation is judgment-based and applied via R0–R5 (per the Classification above) in an apply session.
| Site shape | Disposition | |---|---| | `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | apply (guided) → R1–R5 | | Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here *(arrives via `event-migration` redirect or migration handoff — this pattern's own detector flags only the modern API)* | apply (guided) → R0 then R1–R5 | | `implements ResourceChangeListener`, `onChange()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` |
---
Complete example — before and after
Before (legacy JCR `EventListener` with inline logic and admin resolver)
package com.example.listeners;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
@Component(immediate = true)
@Service
public class ACLPolicyListener implements EventListener {
@Reference
private ResourceResolverFactory resolverFactory;
@Override
public void onEvent(EventIterator events) {
try {Read more
name: resource-change-listener description: AEM Cloud Service expert skill for Sling ResourceChangeListener. Covers the lightweight listener + JobConsumer pattern, migration from javax.jcr.observation.EventListener and resource-topic OSGi EventHandler, ResourceChangeListener vs ExternalResourceChangeListener decision, OSGi filter configuration, review checklist, troubleshooting (silent failures, hot-path blocking, missing service-user mapping), and common pitfalls. license: Apache-2.0
Resource Change Listener — AEM as a Cloud Service
Overview
`org.apache.sling.api.resource.observation.ResourceChangeListener` is the preferred API on AEM CS for reacting to repository content changes. The listener runs on a shared Sling thread and should return as quickly as possible.
For any blocking, repository-intensive, network, workflow, replication, indexing, asset-processing, or otherwise non-trivial work, enqueue a Sling Job and perform the processing in a `JobConsumer`.
Very small in-memory operations (metrics, counters, simple event translation, lightweight filtering, or enqueueing a job) may remain inside `onChange()` provided they do not perform repository access, network I/O, workflow operations, or expensive computation.
Two interface variants:
| Interface | Receives | Use when | |-----------|----------|----------| | `ResourceChangeListener` | Local changes only (same JVM) | Post-processing what *this* pod just wrote | | `ExternalResourceChangeListener` (extends RCL) | Local **and** external (other cluster nodes) | Cluster-wide reactions: cache invalidation, replication follow-ups |
Three CS-specific constraints every listener must satisfy:
| Constraint | Why | |-----------|-----| | No `ResourceResolver` / `Session` / JCR ops inside `onChange()` | Blocks the shared listener thread; delays every other registered listener | | `getServiceResourceResolver(SUBSERVICE)` in the consumer | `getAdministrativeResourceResolver` is removed from the CS SDK | | Filter via `PATHS` / `CHANGES` OSGi properties — not in code | Sling delivers only matching events; in-code filtering wastes the listener thread |
> **`PATHS` are chosen by the implementer from the business scope.** Do not ask the customer for raw `PATHS` syntax (`/content/dam`, `glob:/**/jcr:content/*`, etc.) unless the business scope itself is unclear. Derive the path from what the listener is supposed to react to.
---
Classification — choose before making any changes
**Already implements `ResourceChangeListener`** and `onChange()` only enqueues jobs → Already compliant — verify against the [Review Checklist](#review-checklist) only.
**Already implements `ResourceChangeListener`** and `onChange()` contains business logic (resolver, JCR ops, heavy processing) → Apply **R1–R5** (skip R0).
**Implements `javax.jcr.observation.EventListener`** or **`EventHandler`** subscribed to `org/apache/sling/api/resource/Resource/*` → Apply **R0 then R1–R5**.
**Implements `EventHandler`** subscribed to replication / workflow / custom topics → Use the `event-migration` skill instead — not this one.
**One pattern per session.** If the bundle has multiple legacy listeners, migrate one class at a time.
**Before starting:** Read [`../references/aem-cloud-service-pattern-prerequisites.md`](../references/aem-cloud-service-pattern-prerequisites.md) and apply SCR→DS, service-user, and SLF4J fixes if present in the same changeset.
---
Discovery
Detection is performed by the analyzer ([`../scripts/analyze.sh`](../scripts/README.md)), run by the runbook:
bash ../scripts/analyze.sh <workspace-root> --pattern resource-change-listener
**Match criteria (what the detector flags):** a class that **`implements org.apache.sling.api.resource.observation.ResourceChangeListener`** or **`ExternalResourceChangeListener`** (import-aware) — the modern API, flagged for review against the lightweight + JobConsumer contract.
Emitted at the class declaration, with the class header as the snippet. Parse-level only — direct `implements` clause; reached-via-base-class is not resolved. Legacy `javax.jcr.observation.EventListener` is detected by the `event-migration` pattern (matching the BPA subtype taxonomy); when its logic is plain content observation, that guide routes it back here. A class implementing both `ResourceChangeListener` and an event interface is flagged by both patterns (rare).
Resolution contract
**guided** — `apply (guided)`. The analyzer locates each `ResourceChangeListener`; remediation is judgment-based and applied via R0–R5 (per the Classification above) in an apply session.
| Site shape | Disposition | |---|---| | `implements ResourceChangeListener`, `onChange()` does repository/JCR/heavy work | apply (guided) → R1–R5 | | Legacy `javax.jcr.observation.EventListener` / resource-topic `EventHandler` routed here *(arrives via `event-migration` redirect or migration handoff — this pattern's own detector flags only the modern API)* | apply (guided) → R0 then R1–R5 | | `implements ResourceChangeListener`, `onChange()` only enqueues a Sling Job | skipped: `already-compliant` | | Test code (`src/test/`) | skipped: `test-scope` |
---
Complete example — before and after
Before (legacy JCR `EventListener` with inline logic and admin resolver)
package com.example.listeners;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
@Component(immediate = true)
@Service
public class ACLPolicyListener implements EventListener {
@Reference
private ResourceResolverFactory resolverFactory;
@Override
public void onEvent(EventIterator events) {
try {Repo: adobe/skills
Other skills on adobe-skills.
- /aa-conversion-funnel-analysis
Analyzes a multi-step conversion funnel to find where visitors drop off and which steps have the worst leakage. Use this skill when someone describes a journey and asks about conversion rates, drop-off, fallout, or step completion. Trigger for "analyze our checkout funnel,"
Open skill - /aa-executive-briefing
Generates a concise, executive-ready performance summary covering key metrics, trends, and what's driving movement. Use this skill when someone needs to produce a briefing, executive summary, performance narrative, or stakeholder readout — for example, "write an exec summary of
Open skill - /aa-kpi-pulse
Produces a compact KPI digest showing how key metrics changed over a period and what's driving the movement. Use this skill when someone asks for a performance summary, a weekly recap, a morning briefing, a KPI update, or any variation of "how did we do this week/month." Also
Open skill - /aa-segment-performance-comparator
Compares the performance of two or more audience segments across key metrics side by side. Use this skill when someone wants to compare audiences or visitor groups — for example, "how do mobile visitors compare to desktop on conversion," "compare new vs. returning visitors,"
Open skill - /aa-top-movers-watchlist
Identifies which items (pages, campaigns, products, channels, regions) had the biggest increases or decreases for a key metric between two time periods. Use this skill when someone asks "what's up and what's down," "which campaigns moved the most," "top gainers and losers,"
Open skill - /cja-dimension-analysis
Comprehensive dimension analysis and reporting for CJA. Use this skill whenever the user wants to analyze one or more dimensions — including cardinality, distribution/skew, trends, anomalies, data quality errors, comparisons, and forecasting. Also trigger when someone asks "what
Open skill

