diff --git a/README.md b/README.md index 94bd994..a11c62d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Repository of Adobe skills for AI coding agents. # Install all AEM as a Cloud Service skills (create-component + workflow + dispatcher) in one command /plugin install aem-cloud-service@adobe-skills -# Install all AEM 6.5 LTS skills (workflow + dispatcher) in one command +# Install all AEM 6.5 LTS skills (workflow + dispatcher + replication) in one command /plugin install aem-6-5-lts@adobe-skills ``` @@ -29,7 +29,7 @@ npx skills add https://github.com/adobe/skills/tree/main/skills/aem/edge-deliver # Install all AEM as a Cloud Service skills (create-component + workflow + dispatcher) in one command npx skills add https://github.com/adobe/skills/tree/beta/skills/aem/cloud-service --all -# Install all AEM 6.5 LTS skills (workflow + dispatcher) in one command +# Install all AEM 6.5 LTS skills (workflow + dispatcher + replication) in one command npx skills add https://github.com/adobe/skills/tree/beta/skills/aem/6.5-lts --all # Install for a single agent (pick ONE flavor only) @@ -57,7 +57,7 @@ gh upskill adobe/skills --path skills/aem/edge-delivery-services --all # Install all AEM as a Cloud Service skills (create-component + workflow + dispatcher) gh upskill adobe/skills --path skills/aem/cloud-service --all -# Install all AEM 6.5 LTS skills (workflow + dispatcher) +# Install all AEM 6.5 LTS skills (workflow + dispatcher + replication) gh upskill adobe/skills --path skills/aem/6.5-lts --all # Install a specific skill @@ -139,6 +139,28 @@ Current dispatcher flavors: Each flavor contains parallel capability groups (workflow orchestration, config authoring, technical advisory, incident response, performance tuning, and security hardening). Shared advisory logic is centralized under each flavor's `dispatcher/shared/references/` to reduce duplication and drift. +### AEM Replication + +Replication skills for AEM 6.5 LTS cover the full content distribution lifecycle from agent configuration to troubleshooting. + +**Location:** `skills/aem/6.5-lts/skills/aem-replication` + +The aem-replication skill contains four specialist sub-skills: + +| Sub-Skill | Purpose | +|---|---| +| `configure-replication-agent` | Configure replication agents for publishing, dispatcher flush, and reverse replication | +| `replicate-content` | Activate and deactivate content using UI, workflows, and package manager | +| `replication-api` | Use the Replication API programmatically in custom code with complete Java examples | +| `troubleshoot-replication` | Diagnose and fix blocked queues, connectivity failures, and distribution problems | + +**Key features:** +- All skills based on official AEM 6.5 LTS documentation +- Complete coverage of public Replication API (Replicator, ReplicationOptions, AgentManager, ReplicationQueue, etc.) +- 49 Java code examples for OSGi services, servlets, and workflow steps +- 12+ troubleshooting scenarios with step-by-step resolution +- 3,575 lines of comprehensive documentation + ### AEM as a Cloud Service — Best Practices & Migration Under `skills/aem/cloud-service/skills/`, **`best-practices/`** is the **general-purpose** Cloud Service skill: pattern modules, Java baseline references (SCR→OSGi DS, resolver/logging, and related refs), and day-to-day Cloud Service alignment. Use it **without** loading **migration** for greenfield or maintainability work. **`migration/`** (BPA/CAM orchestration) is **scoped to legacy AEM → AEM as a Cloud Service** (not Edge Delivery or 6.5 LTS); it **delegates** concrete refactors to **`best-practices`** (`references/`). **Installing the AEM as a Cloud Service plugin** (`aem-cloud-service`, or the `skills/aem/cloud-service` path with `npx skills` / `gh upskill`) **includes both**; the agent should load the appropriate `SKILL.md` for the task. Use **`gh upskill` / `npx skills` with `--skill`** when you need a specific bundled skill (see **Installation** above). @@ -233,17 +255,28 @@ skills/ | |-- workflow-debugging/ | |-- workflow-triaging/ | \-- workflow-orchestrator/ - |-- ensure-agents-md/ - \-- dispatcher/ - |-- SKILL.md <-- discovered by npx skills (router) - |-- config-authoring/ - | |-- SKILL.md <-- specialist (bundled inside dispatcher) - | \-- references/ - |-- technical-advisory/ - |-- incident-response/ - |-- performance-tuning/ - |-- security-hardening/ - \-- workflow-orchestrator/ + |-- dispatcher/ + | |-- SKILL.md <-- discovered by npx skills (router) + | |-- config-authoring/ + | | |-- SKILL.md <-- specialist (bundled inside dispatcher) + | | \-- references/ + | |-- technical-advisory/ + | |-- incident-response/ + | |-- performance-tuning/ + | |-- security-hardening/ + | \-- workflow-orchestrator/ + |-- aem-replication/ + | |-- README.md + | |-- SKILL.md <-- discovered by npx skills (router) + | |-- configure-replication-agent/ + | | \-- SKILL.md <-- specialist (bundled inside aem-replication) + | |-- replicate-content/ + | | \-- SKILL.md <-- specialist (bundled inside aem-replication) + | |-- replication-api/ + | | \-- SKILL.md <-- specialist (bundled inside aem-replication) + | \-- troubleshoot-replication/ + | \-- SKILL.md <-- specialist (bundled inside aem-replication) + \-- ensure-agents-md/ ``` ## Contributing diff --git a/skills/aem/6.5-lts/.claude-plugin/plugin.json b/skills/aem/6.5-lts/.claude-plugin/plugin.json index e90376e..dbe00cd 100644 --- a/skills/aem/6.5-lts/.claude-plugin/plugin.json +++ b/skills/aem/6.5-lts/.claude-plugin/plugin.json @@ -1,11 +1,11 @@ { "name": "aem-6-5-lts", - "description": "All AEM 6.5 LTS skills: Workflow model design, development, triggering, launchers, debugging, and triaging, plus Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening for AEM 6.5 LTS and AMS environments.", + "description": "All AEM 6.5 LTS skills: Workflow model design, development, triggering, launchers, debugging, and triaging; Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening; and Replication agent configuration, content activation, API usage, and troubleshooting for AEM 6.5 LTS and AMS environments.", "version": "1.0.0", "author": { "name": "Adobe" }, "repository": "https://github.com/adobe/skills", "license": "Apache-2.0", - "keywords": ["aem", "aem6.5", "6.5-lts", "ams", "dispatcher", "httpd", "apache", "workflow", "adobe"] + "keywords": ["aem", "aem6.5", "6.5-lts", "ams", "dispatcher", "httpd", "apache", "workflow", "replication", "adobe"] } diff --git a/skills/aem/6.5-lts/skills/aem-replication/README.md b/skills/aem/6.5-lts/skills/aem-replication/README.md new file mode 100644 index 0000000..fd301e1 --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/README.md @@ -0,0 +1,99 @@ +# AEM 6.5 LTS Replication Skills + +Comprehensive replication skills for Adobe Experience Manager 6.5 LTS, covering agent configuration, content activation, programmatic API usage, and troubleshooting. + +## Skills Included + +### 1. Configure Replication Agent +Configure replication agents for content publishing, dispatcher cache flushing, and reverse replication. + +**Key capabilities:** +- Default agent setup (Author → Publish) +- Dispatcher Flush agent configuration +- Reverse replication (Publish → Author) +- Multiple publish instance configuration +- Security best practices + +**Typical use cases:** +- Initial AEM instance setup +- Adding new publish instances +- Setting up disaster recovery +- Configuring load-balanced environments + +### 2. Replicate Content +Activate and deactivate content using various methods from simple Quick Publish to advanced workflows. + +**Key capabilities:** +- Quick Publish for simple activation +- Manage Publication for advanced control +- Tree Activation for hierarchical publishing +- Package-based replication +- Workflow-based approval publishing +- Scheduled activation/deactivation +- DAM asset replication + +**Typical use cases:** +- Daily content publishing operations +- Marketing campaign launches +- Scheduled content releases +- Bulk content migration + +### 3. Replication API +Use the AEM 6.5 LTS Replication API for programmatic content distribution in custom code. + +**Key capabilities:** +- Replicator interface methods +- ReplicationOptions configuration +- ReplicationStatus queries +- AgentManager for agent inspection +- ReplicationQueue management +- ReplicationListener for event monitoring +- Complete Java code examples + +**Typical use cases:** +- Custom OSGi services +- Workflow process steps +- Servlets with replication logic +- Bulk content operations +- Integration with external systems + +### 4. Troubleshoot Replication +Diagnose and fix common replication issues including blocked queues and connectivity failures. + +**Key capabilities:** +- Blocked queue diagnosis and resolution +- Connection error troubleshooting +- Authentication and SSL issues +- Dispatcher cache invalidation problems +- Event queue management +- Performance optimization + +**Typical use cases:** +- Production incidents +- Content not appearing on Publish +- Slow or stuck replication +- Agent configuration issues +- Queue management + +## Documentation Sources + +All skills are based on official Adobe AEM 6.5 LTS documentation: + +- **Official Replication Guide**: https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication +- **Troubleshooting Guide**: https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep +- **API Reference**: https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html + +## Getting Started + +1. **For first-time setup**: Start with [Configure Replication Agent](./configure-replication-agent/SKILL.md) +2. **For daily operations**: Use [Replicate Content](./replicate-content/SKILL.md) +3. **For custom code**: Reference [Replication API](./replication-api/SKILL.md) +4. **For issues**: Consult [Troubleshoot Replication](./troubleshoot-replication/SKILL.md) + +## Total Documentation + +- **4 comprehensive skills** +- **3,575 lines** of documentation +- **49 Java code examples** +- **12+ common troubleshooting scenarios** +- **100% based on official Adobe documentation** diff --git a/skills/aem/6.5-lts/skills/aem-replication/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/SKILL.md new file mode 100644 index 0000000..1f1884a --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/SKILL.md @@ -0,0 +1,133 @@ +--- +name: aem-replication +description: | + Single entry point for all AEM 6.5 LTS Replication skills. Covers configuring replication agents, + activating/deactivating content, using the Replication API programmatically, and troubleshooting + distribution issues for Adobe Experience Manager 6.5 LTS. +license: Apache-2.0 +compatibility: Requires AEM 6.5 LTS or Adobe Managed Services (AMS). NOT compatible with AEM as a Cloud Service (use Sling Distribution API instead). +metadata: + version: "1.0" + aem_version: "6.5 LTS" +--- + +# AEM 6.5 LTS Replication + +Route user requests to the appropriate specialist skill based on intent. + +## Intent Router + +| User Intent | Skill | Path | +|---|---|---| +| Configure replication agents (default, dispatcher flush, reverse replication) | Configure Replication Agent | [configure-replication-agent/SKILL.md](./configure-replication-agent/SKILL.md) | +| Activate or deactivate content using UI or workflows | Replicate Content | [replicate-content/SKILL.md](./replicate-content/SKILL.md) | +| Use Replication API programmatically in custom code | Replication API | [replication-api/SKILL.md](./replication-api/SKILL.md) | +| Diagnose blocked queues, connectivity issues, or distribution problems | Troubleshoot Replication | [troubleshoot-replication/SKILL.md](./troubleshoot-replication/SKILL.md) | +| End-to-end workflows: new environment setup, incident response, performance optimization | Replication Orchestrator | [replication-orchestrator/SKILL.md](./replication-orchestrator/SKILL.md) | + +## How to Use + +1. Match the user's request to one row in the Intent Router table above. +2. Read the linked SKILL.md for that specialist skill. +3. Follow the workflow and guidance defined in that skill. +4. For complex scenarios spanning multiple skills (e.g., configure agent then troubleshoot), start with the primary intent and cross-reference as needed. + +## Skill Overview + +### Configure Replication Agent + +Set up and configure replication agents for: +- **Default agents**: Author to Publish content distribution +- **Dispatcher Flush agents**: Cache invalidation +- **Reverse replication**: Publish to Author user-generated content flow +- **Multiple publish instances**: Load balancing and high availability + +**When to use:** First-time setup, adding new publish instances, reconfiguring agents + +### Replicate Content + +Activate and deactivate content through: +- **Quick Publish**: Simple one-click activation +- **Manage Publication**: Advanced scheduling and approval workflows +- **Tree Activation**: Hierarchical bulk publishing +- **Package Manager**: Specific content set distribution +- **Workflows**: Approval-based publishing +- **Scheduled Activation**: Time-based content publishing + +**When to use:** Publishing pages, assets, or DAM content; unpublishing content + +### Replication API + +Programmatic replication using official AEM 6.5 LTS public APIs: +- **Replicator interface**: Core replication methods +- **ReplicationOptions**: Configure synchronous/asynchronous, agent filtering +- **ReplicationStatus**: Query replication state +- **AgentManager, ReplicationQueue, ReplicationListener**: Advanced queue management and monitoring + +**When to use:** Custom code integration, bulk operations, workflow process steps, servlets + +### Troubleshoot Replication + +Diagnose and fix common issues: +- **Blocked queues**: FIFO queue failures +- **Connection errors**: Network, authentication, SSL issues +- **Content not appearing**: Dispatcher cache, permissions +- **Agent configuration**: URI, credentials, triggers +- **Event queue issues**: Stuck replication jobs + +**When to use:** Replication failures, performance issues, content not distributing + +### Replication Orchestrator + +Coordinates end-to-end replication workflows spanning multiple sub-skills: +- **New Environment Setup**: Configure agents → Test replication → Troubleshoot +- **Production Incident Response**: Diagnose → Fix → Verify +- **Performance Optimization**: Monitor → Tune → Validate +- **Migration Preparation**: Audit → Plan → Execute + +**When to use:** Multi-step scenarios requiring coordination across configure, replicate, API, and troubleshoot skills + +## Common Workflows + +### First-Time Setup +1. Use **Configure Replication Agent** to set up default agent +2. Use **Replicate Content** to test with a sample page +3. If issues occur, use **Troubleshoot Replication** + +### Production Operations +1. Use **Replicate Content** for day-to-day publishing +2. Use **Replication API** for automated/bulk operations +3. Use **Troubleshoot Replication** when issues arise + +### Advanced Integration +1. Use **Replication API** to understand available methods +2. Use **Configure Replication Agent** to understand agent configuration +3. Use **Troubleshoot Replication** for debugging custom replication code + +## Foundation References + +Shared reference materials used across all replication skills: + +- **[Agent Types](./references/replication-foundation/agent-types.md)**: Default, Dispatcher Flush, Reverse, and Static agents +- **[Queue Mechanics](./references/replication-foundation/queue-mechanics.md)**: FIFO processing, retry logic, queue management +- **[AEM 6.5 LTS Guardrails](./references/replication-foundation/65-lts-guardrails.md)**: Service users, timeouts, batch limits, best practices +- **[API Quick Reference](./references/replication-foundation/api-reference.md)**: Replicator, ReplicationOptions, ReplicationStatus methods + +## Official Documentation + +All skills reference official Adobe AEM 6.5 LTS documentation: +- [Replication Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication) +- [Replication Troubleshooting](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep) +- [Replication API JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) + +## Related Skills + +- **AEM Workflow**: Integrate replication with approval workflows +- **Dispatcher**: Configure Dispatcher Flush agents for cache invalidation + +## Migration to AEM as a Cloud Service + +AEM as a Cloud Service uses the **Sling Distribution API** instead of replication agents. If planning migration: +- Review [Cloud Service Distribution Documentation](https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/operations/distribution.html) +- For code migration patterns, see `skills/aem/cloud-service/skills/best-practices/references/replication.md` +- Avoid agent-specific coupling (filter by agent ID) to reduce migration complexity diff --git a/skills/aem/6.5-lts/skills/aem-replication/configure-replication-agent/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/configure-replication-agent/SKILL.md new file mode 100644 index 0000000..53fd623 --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/configure-replication-agent/SKILL.md @@ -0,0 +1,523 @@ +--- +name: configure-replication-agent +description: Configure AEM 6.5 LTS replication agents for content publishing, dispatcher cache flushing, and reverse replication +--- + +# Configure AEM Replication Agent + +This skill guides you through configuring replication agents in Adobe Experience Manager 6.5 LTS. Replication agents are the core mechanism for distributing content from Author to Publish environments, managing Dispatcher cache, and handling user-generated content flows. + +## When to Use This Skill + +Use this skill when you need to: +- Set up new replication agents for Publish instances +- Configure Dispatcher Flush agents for cache invalidation +- Implement Reverse Replication for user-generated content +- Add replication agents for multiple publish instances +- Troubleshoot or reconfigure existing replication agents +- Implement custom replication workflows + +## Prerequisites + +- AEM 6.5 Author instance running (typically port 4502) +- AEM 6.5 Publish instance(s) running (typically port 4503+) +- Administrator access to AEM Author environment +- Network connectivity between Author and Publish instances +- Understanding of your deployment topology (single/multiple publish instances) + +## Replication Agent Types + +AEM supports several replication agent types for different purposes: + +| Agent Type | Purpose | Location | Serialization Type | +|------------|---------|----------|-------------------| +| **Default Agent** | Publishes content from Author to Publish | Author | Default | +| **Dispatcher Flush** | Invalidates Dispatcher cache | Publish | Dispatcher Flush | +| **Reverse Replication** | Returns user input from Publish to Author | Author (polling) + Publish (outbox) | Default | +| **Static Agent** | Stores static node representation to filesystem | Author/Publish | Static Content Builder | + +## Configuration Workflow + +### Step 1: Access Replication Agent Configuration + +**Classic UI:** +1. Navigate to `http://localhost:4502/etc/replication/agents.author.html` +2. Or go to Tools → Replication → Agents on author + +**Touch UI:** +1. Navigate to Tools → Deployment → Replication +2. Select "Agents on author" + +### Step 2: Create or Edit Replication Agent + +**For new agent:** +1. Click "New..." or "Create" button +2. Enter a unique name (e.g., `publish_instance_1`, `dispatcher_flush`) +3. Select template based on agent type +4. Click "Create" + +**For existing agent:** +1. Select the agent from the list +2. Click "Edit" to open configuration + +### Step 3: Configure Settings Tab + +Essential settings to configure: + +``` +Field: Enabled +Value: ✓ (checked) +Purpose: Activates the replication agent + +Field: Serialization Type +Values: + - Default (standard content replication) + - Dispatcher Flush (cache invalidation only) + - Static Content Builder (filesystem storage) +Purpose: Determines how content is packaged + +Field: Retry Delay (ms) +Value: 60000 (default) +Purpose: Time between retry attempts on failure + +Field: Agent User Id +Value: replication-service (create dedicated user) +Purpose: Service account with minimal required permissions +WARNING: Never use 'admin' account in production + +Field: Log Level +Values: Error | Info | Debug +Purpose: Controls logging verbosity for troubleshooting +``` + +**Security Best Practice:** +Create a dedicated replication service account: +1. Navigate to Security → Users +2. Create new user `replication-service` +3. Grant minimum permissions: read on source paths, replicate privilege +4. Set this user in "Agent User Id" field + +### Step 4: Configure Transport Tab + +Configure connection details to target instance: + +``` +Field: URI +Format: http[s]://:/bin/receive?sling:authRequestLogin=1 +Examples: + - http://localhost:4503/bin/receive?sling:authRequestLogin=1 + - https://publish1.example.com:4503/bin/receive?sling:authRequestLogin=1 + +Field: User +Value: admin (or dedicated replication receiver account) +Purpose: Authentication to Publish instance + +Field: Password +Value: [secure password] +Purpose: Authentication credentials + +Field: OAuth Settings +Value: Leave empty unless using OAuth +Purpose: Alternative authentication mechanism + +Field: NTLM Domain/Host/User/Password +Value: Configure if using Windows NTLM authentication +Purpose: Domain-based authentication + +Field: SSL +Options: + - Relaxed SSL: Allow self-signed certificates (development only) + - Allow expired: Accept expired certificates (not recommended) +Purpose: SSL/TLS configuration +``` + +**Connection String Examples:** + +| Scenario | URI Pattern | +|----------|-------------| +| Local Publish | `http://localhost:4503/bin/receive?sling:authRequestLogin=1` | +| Remote Publish | `https://publish.example.com:4503/bin/receive?sling:authRequestLogin=1` | +| Multiple Instances | Create separate agents for each: port 4503, 4504, 4505, etc. | + +### Step 5: Configure Proxy Tab (Optional) + +Only configure if network routing requires proxy: + +``` +Field: Proxy Host +Value: proxy.corporate.com + +Field: Proxy Port +Value: 8080 + +Field: Proxy User/Password +Value: [proxy credentials if required] +``` + +### Step 6: Configure Extended Tab + +Advanced HTTP settings: + +``` +Field: HTTP Method +Values: GET (default) | POST +Purpose: HTTP verb for replication requests + +Field: HTTP Headers +Default headers: + CQ-Action:{action} + CQ-Handle:{path} + CQ-Path:{path} +Purpose: Custom headers for replication requests + +Field: Connection Timeout (ms) +Value: 10000 (default) +Purpose: Maximum time to establish connection + +Field: Socket Timeout (ms) +Value: 10000 (default) +Purpose: Maximum time waiting for data + +Field: Protocol Version +Values: HTTP/1.0 | HTTP/1.1 +Default: HTTP/1.0 +Purpose: HTTP protocol version +``` + +### Step 7: Configure Triggers Tab + +Control when the agent activates: + +``` +Option: Ignore default +Effect: Excludes agent from default replication +Use case: Custom workflows only + +Option: On Modification +Effect: Auto-activates on page changes +Use case: Automatic publishing workflows + +Option: On Distribute +Effect: Triggers on distribution events +Use case: Package replication + +Option: On Receive +Effect: Chains replication from incoming events +Use case: Multi-tier replication + +Option: No Status Update +Effect: Doesn't update replication status +Use case: Read-only monitoring agents + +Option: No Versioning +Effect: Skips version creation +Use case: Performance optimization +``` + +### Step 8: Test Connection + +**Before enabling:** +1. Click "Test Connection" button in Transport tab +2. Verify success message: "Replication test succeeded" +3. Check for any authentication or connectivity errors + +**Common test failures:** + +| Error | Cause | Solution | +|-------|-------|----------| +| Connection refused | Publish instance not running | Start Publish instance | +| 401 Unauthorized | Invalid credentials | Verify user/password | +| Timeout | Network/firewall issue | Check connectivity, adjust timeouts | +| SSL handshake failed | Certificate issue | Configure SSL settings or update certificates | + +### Step 9: Enable and Save + +1. Ensure "Enabled" checkbox is checked on Settings tab +2. Click "OK" to save configuration +3. Agent status should show green indicator (idle/ready state) + +## Agent-Specific Configuration + +### Default Replication Agent (Author to Publish) + +**Purpose:** Publishes content from Author to Publish environment + +**Configuration specifics:** +- Serialization Type: **Default** +- Transport URI: `http://publish-host:4503/bin/receive?sling:authRequestLogin=1` +- Location: Author instance (`/etc/replication/agents.author`) +- Triggers: Enable "On Modification" for automatic publishing +- User: Dedicated replication-service account with read permissions + +**Multiple Publish Instances:** +Create one agent per Publish instance: +- Agent 1: `publish_instance_1` → `http://publish1:4503/bin/receive` +- Agent 2: `publish_instance_2` → `http://publish2:4504/bin/receive` +- Agent 3: `publish_instance_3` → `http://publish3:4505/bin/receive` + +### Dispatcher Flush Agent + +**Purpose:** Invalidates Dispatcher cache when content is published + +**Configuration specifics:** +- Serialization Type: **Dispatcher Flush** +- Transport URI: `http://dispatcher-host:80/dispatcher/invalidate.cache` +- Location: Publish instance (`/etc/replication/agents.publish`) +- HTTP Method: GET or POST (depending on Dispatcher config) +- User: May not require authentication depending on network setup + +**Important settings:** +``` +Settings Tab: + - Serialization Type: Dispatcher Flush + - Enabled: ✓ + +Transport Tab: + - URI: http://dispatcher:80/dispatcher/invalidate.cache + - (Authentication may not be required) + +Extended Tab: + - HTTP Headers: Add custom headers if Dispatcher requires them +``` + +**Dispatcher configuration requirements:** +Ensure Dispatcher `dispatcher.any` has: +``` +/allowedClients { + /0 { /type "allow" /glob "*publish-instance-ip*" } +} +``` + +### Reverse Replication Agent + +**Purpose:** Collects user-generated content from Publish and sends to Author + +**Requires TWO components:** + +**1. Outbox Agent on Publish** (passive collection point) +- Location: `/etc/replication/agents.publish/outbox` +- Serialization Type: Default +- Enabled: ✓ +- No transport configuration needed (local collection only) + +**2. Reverse Replication Agent on Author** (active polling) +- Location: `/etc/replication/agents.author/reverse_replication` +- Serialization Type: Default +- Transport URI: `http://publish-host:4503/bin/receive?sling:authRequestLogin=1` +- Triggers: Enable polling triggers +- The Author agent periodically polls the Publish outbox + +**Configuration steps:** + +1. **On Publish instance:** + - Navigate to `/etc/replication/agents.publish` + - Verify "Outbox" agent exists and is enabled + - No additional configuration needed + +2. **On Author instance:** + - Create new replication agent named "reverse_replication" + - Settings Tab: Serialization Type = Default, Enabled = ✓ + - Transport Tab: URI = `http://publish:4503/bin/receive` + - Save configuration + +**Content requirements for reverse replication:** +Only `cq:Page` nodes are supported out-of-the-box. For other node types, custom implementation required. + +To trigger reverse replication, add properties: +- `cq:distribute` (Boolean) = true +- `cq:lastModified` (Date) +- `cq:lastModifiedBy` (String) + +## Monitoring Replication Agents + +### Agent Status Indicators + +| Status | Indicator | Meaning | Action | +|--------|-----------|---------|--------| +| Idle | Green | Queue empty, ready | Normal operation | +| Active | Green | Processing queue | Normal operation | +| Blocked | Red | Queue blocked by failed item | Investigate failure, clear queue | +| Disabled | Gray | Agent not enabled | Enable if needed | + +### Access Agent Queue + +1. Click agent name in agents list +2. View queue status and pending items +3. Options available: + - **Refresh**: Update queue display + - **Clear**: Remove all queued items + - **Force Retry**: Retry first item in queue + - **View Log**: Check replication logs + +### Monitor via JMX Console + +Navigate to JMX Console: `http://localhost:4502/system/console/jmx` + +Search for: `com.day.cq.replication:type=Agent` + +Monitor: +- Queue size +- Replication status +- Error counts +- Last replication timestamp + +## Validation and Testing + +### Test Replication Flow + +1. **Create test page** on Author: + - Navigate to Sites console + - Create new page under `/content/test` + - Add some content + +2. **Activate the page:** + - Select page + - Click "Quick Publish" or "Manage Publication" + - Verify success message + +3. **Check replication status:** + - Page properties should show "Published" status + - Green checkmark indicator + +4. **Verify on Publish:** + - Navigate to `http://publish:4503/content/test/page-name.html` + - Content should be visible + - Check timestamp matches activation time + +5. **Check agent queue:** + - Open replication agent + - Queue should be empty after successful replication + - Log should show success entries + +### Dispatcher Cache Validation + +1. **After content activation:** + - Check Dispatcher cache directory + - Verify cached files are invalidated/updated + - Check Dispatcher logs for invalidation requests + +2. **Test cache flush:** + - Activate a page + - Verify Dispatcher Flush agent processes request + - Check Dispatcher cache is properly invalidated + +## Troubleshooting + +### Common Issues and Solutions + +**Issue: Replication queue blocked** +- **Symptom:** Red indicator, items stuck in queue +- **Diagnosis:** First item in queue failed, blocking subsequent items +- **Solution:** + 1. Click agent to view queue + 2. Check error message on failed item + 3. Fix underlying issue (connectivity, permissions, target instance down) + 4. Click "Force Retry" or "Clear" failed item + 5. Verify remaining items process + +**Issue: Connection refused** +- **Symptom:** "Connection refused" in agent test or logs +- **Diagnosis:** Publish instance not reachable +- **Solution:** + 1. Verify Publish instance is running: `http://publish:4503/system/console` + 2. Check network connectivity: `telnet publish-host 4503` + 3. Verify firewall rules allow connection + 4. Check URI in Transport tab is correct + +**Issue: 401 Unauthorized** +- **Symptom:** Authentication failures in replication +- **Diagnosis:** Invalid credentials or insufficient permissions +- **Solution:** + 1. Verify user exists on target instance + 2. Check password is correct + 3. Verify user has replication permissions + 4. Test credentials manually: `curl -u user:password http://publish:4503/bin/receive` + +**Issue: SSL handshake failure** +- **Symptom:** SSL/TLS errors in logs +- **Diagnosis:** Certificate issues or SSL misconfiguration +- **Solution:** + 1. For development: Enable "Relaxed SSL" option + 2. For production: Install proper certificates in Java keystore + 3. Verify certificate chain is complete + 4. Check SSL protocol compatibility + +**Issue: Dispatcher not flushing cache** +- **Symptom:** Old content served after activation +- **Diagnosis:** Dispatcher Flush agent not working +- **Solution:** + 1. Verify Dispatcher Flush agent enabled on Publish + 2. Check agent Transport URI matches Dispatcher endpoint + 3. Verify Dispatcher `/allowedClients` configuration + 4. Check Dispatcher logs for invalidation requests + 5. Manually test: `curl -X POST http://dispatcher/dispatcher/invalidate.cache` + +**Issue: User data not synchronizing** +- **Symptom:** User accounts not replicating to Publish +- **Diagnosis:** Attempting to replicate user data via standard replication +- **Solution:** + - User data is NOT replicated by default agents + - Configure User Synchronization separately + - Navigate to: Configuration → User Synchronization + - Follow Adobe documentation for user sync setup + +## Security Best Practices + +1. **Never use admin account for replication** + - Create dedicated `replication-service` user + - Grant minimum required permissions + - Limit access to specific content paths + +2. **Use HTTPS for production** + - Configure SSL certificates + - Enable secure transport + - Avoid "Relaxed SSL" in production + +3. **Implement Mutual SSL (MSSL) for enhanced security** + - Certificate-based authentication + - Bidirectional trust verification + - Follow Adobe MSSL configuration guide + +4. **Restrict agent configuration access** + - Limit permissions on `/etc/replication` node + - Only administrators should configure agents + - Audit configuration changes + +5. **Monitor and log replication activity** + - Enable appropriate log levels + - Review logs regularly for failures + - Set up alerts for blocked queues + +6. **Network security** + - Use firewalls to restrict replication traffic + - Implement VPN for cross-datacenter replication + - Whitelist IP addresses + +## Related Skills + +- `replicate-content`: Activate and deactivate content using configured agents +- `troubleshoot-replication`: Diagnose and fix replication issues +- For AEM as a Cloud Service, see content distribution documentation (different architecture) + +## Success Criteria + +- ✓ Replication agent created and enabled +- ✓ Test connection succeeds +- ✓ Agent status shows green (idle/ready) +- ✓ Test page successfully replicates to Publish +- ✓ Content appears on Publish instance +- ✓ Agent queue processes items without blocking +- ✓ Logs show successful replication entries +- ✓ Dedicated service account configured (not admin) +- ✓ For Dispatcher Flush: cache invalidates on activation +- ✓ For Reverse Replication: both Outbox and polling agents configured + +## Additional Resources + +- [Official AEM 6.5 LTS Replication Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication) +- [AEM 6.5 LTS Replication Troubleshooting Guide](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep) +- [Replication API JavaDoc (Package Summary)](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) +- [AEM 6.5 LTS Documentation Hub](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts) +- [AEM Replication Cookbook (Community)](https://aemlounge.wordpress.com/2018/03/19/a-cookbook-for-replication-in-aem/) +- AEM 6.5 Operations Dashboard for monitoring +- Dispatcher documentation for cache invalidation configuration diff --git a/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/65-lts-guardrails.md b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/65-lts-guardrails.md new file mode 100644 index 0000000..5dc8b4c --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/65-lts-guardrails.md @@ -0,0 +1,333 @@ +# AEM 6.5 LTS Replication Guardrails + +Best practices, limitations, and operational guidelines for replication in AEM 6.5 LTS. + +## Service User Requirements + +### Never Use Admin Account + +**Prohibited:** +```java +// NEVER DO THIS +authInfo.put(ResourceResolverFactory.USER, "admin"); +authInfo.put(ResourceResolverFactory.PASSWORD, "admin"); +``` + +**Required:** +```java +// Use service user +authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); +resolver = resolverFactory.getServiceResourceResolver(authInfo); +``` + +### Service User Configuration + +**OSGi Config:** `org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-replication.cfg.json` + +```json +{ + "user.mapping": [ + "com.mycompany.aem.core:replication-service=replication-service" + ] +} +``` + +**Required JCR Permissions:** + +Service user `replication-service` needs: +- Read: `/content`, `/conf` +- Modify: `/var/replication/outbox` (reverse replication) +- Replicate: `jcr:all` on replicated paths + +## URI Patterns + +### Valid Transport URIs + +**Author to Publish:** +``` +http://publish-host:4503/bin/receive?sling:authRequestLogin=1 +https://publish-host:4503/bin/receive?sling:authRequestLogin=1 +``` + +**Dispatcher Flush:** +``` +http://dispatcher-host:80/dispatcher/invalidate.cache +``` + +**Reverse Replication:** +``` +http://author-host:4502/bin/receive?sling:authRequestLogin=1 +``` + +### Invalid Patterns + +Avoid these common mistakes: +- Missing `/bin/receive` path +- Missing `sling:authRequestLogin=1` query parameter +- Using `localhost` instead of actual hostname +- Incorrect port numbers (4502 is Author, 4503 is Publish) + +## Log Locations + +### AEM 6.5 On-Premise + +- **Error Log:** `/crx-quickstart/logs/error.log` +- **Replication Log:** `/crx-quickstart/logs/replication.log` +- **Request Log:** `/crx-quickstart/logs/request.log` + +### AEM 6.5 on AMS + +- **Cloud Manager:** Download logs via Cloud Manager UI +- **SSH Access:** Requires credentials and permissions +- **Log Streaming:** Not available (manual download only) + +### Log Level Configuration + +Navigate to: `/system/console/slinglog` + +Add logger for: +- **Logger Name:** `com.day.cq.replication` +- **Log Level:** INFO (production), DEBUG (troubleshooting) +- **Log File:** `logs/replication.log` + +## Timeout Configuration + +### Default Values + +- **Connection Timeout:** 10000ms (10 seconds) +- **Socket Timeout:** 10000ms (10 seconds) +- **Retry Delay:** 60000ms (1 minute) + +### When to Increase Timeouts + +**Large DAM Assets:** +- Connection Timeout: 30000ms (30 seconds) +- Socket Timeout: 60000ms (1 minute) + +**Slow Networks (WAN):** +- Connection Timeout: 20000ms (20 seconds) +- Socket Timeout: 30000ms (30 seconds) + +**Do Not Exceed:** +- Connection Timeout: 120000ms (2 minutes) +- Socket Timeout: 300000ms (5 minutes) + +## Batch Size Limits + +### Replication Array Method + +```java +replicator.replicate(session, type, paths[], opts); +``` + +**Recommended Batch Sizes:** +- **Small Pages:** 100-500 paths per call +- **Large Pages/DAM:** 50-100 paths per call +- **Maximum:** 1000 paths (risk of timeout/memory) + +**Performance Impact:** +- Batches > 500: Higher memory usage, longer serialization +- Batches > 1000: Risk of OutOfMemoryError + +### Asynchronous Replication for Bulk + +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSynchronous(false); // Non-blocking +``` + +Use asynchronous for: +- Batch sizes > 100 items +- Background content updates +- Scheduled maintenance operations + +## Resource Lifecycle + +### ResourceResolver Management + +**Caller Responsibility:** +```java +public void activatePage(ResourceResolver resolver, String path) { + // Caller must close resolver + // Do NOT close inside this method +} +``` + +**Try-with-Resources Pattern:** +```java +try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo)) { + replicator.replicate(session, type, path); +} // Auto-closed +``` + +**Manual Close:** +```java +ResourceResolver resolver = null; +try { + resolver = resolverFactory.getServiceResourceResolver(authInfo); + replicator.replicate(session, type, path); +} finally { + if (resolver != null && resolver.isLive()) { + resolver.close(); + } +} +``` + +## Dangerous Paths + +### Never Replicate + +**System Paths:** +- `/apps` - Application code (deploy via package manager) +- `/libs` - AEM system libraries +- `/etc/designs` - Design configurations (deploy via package) +- `/var/audit` - Audit logs +- `/etc/replication` - Replication agent configs + +**Consequences:** +- Configuration drift between Author/Publish +- Overwriting system defaults +- Security vulnerabilities +- Unpredictable behavior + +### Safe Paths + +**Content Paths:** +- `/content` - Site content +- `/content/dam` - Digital assets +- `/content/experience-fragments` - Experience fragments +- `/content/forms` - Forms data + +## Performance Thresholds + +### Queue Depth Monitoring + +**Alerting Thresholds:** +- **Warning:** Queue depth > 20 items for > 5 minutes +- **Critical:** Queue depth > 50 items or blocked > 10 minutes + +**JMX Monitoring:** +``` +com.day.cq.replication:type=Agent,id= + └─ QueueNumEntries (gauge) + └─ QueueBlocked (boolean) +``` + +### Replication Rate + +**Expected Rates:** +- **Small Pages:** 50-100 pages/minute +- **Large Pages:** 20-50 pages/minute +- **DAM Assets:** 10-30 assets/minute (size-dependent) + +**Degradation Indicators:** +- Rate < 10 pages/minute: Investigate target instance +- Increasing queue depth: Check network/capacity +- Repeated retries: Authentication or connectivity issue + +## Version Explosion Prevention + +### Problem + +Excessive replication creates version history growth: +- Each activation creates a version +- Versions stored in `/var/versions` +- High storage consumption + +### Mitigation + +**Suppress Versions:** +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSuppressVersions(true); // No version created +``` + +**Use For:** +- Bulk content updates +- Automated replication jobs +- Non-critical content changes + +**Avoid For:** +- Editorial content (need rollback) +- Legal/compliance content +- Critical business pages + +## Security Considerations + +### Transport Security + +**Use HTTPS For:** +- External-facing publish instances +- Cross-datacenter replication +- Sensitive content replication + +**Configuration:** +``` +https://publish-host:8443/bin/receive?sling:authRequestLogin=1 +``` + +**Certificate Validation:** +- Import target instance certificate into Author truststore +- Verify certificate chain validity +- Monitor certificate expiration + +### Authentication + +**Service User:** Preferred method +**Basic Auth:** Acceptable for internal networks +**Token-Based:** Not supported in 6.5 LTS replication + +### Input Validation + +**Always Validate:** +- Path must start with `/content/` +- Resource type must be replicable (`cq:Page`, `dam:Asset`) +- User has replication permission +- Resource exists + +## Version-Specific Considerations + +### API Deprecation Tracking + +While AEM 6.5 LTS maintains API compatibility throughout the 6.5.x release line, individual point releases may deprecate specific methods or introduce warnings: + +**Check Release Notes for Each 6.5.x Version:** +- **Release Notes:** [AEM 6.5 Service Pack Release Notes](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/release-notes/release-notes) +- **Deprecated APIs:** Review JavaDoc `@Deprecated` annotations for alternative methods +- **Breaking Changes:** While rare in LTS, service packs may deprecate experimental APIs introduced in earlier 6.5.x releases + +**Recommended Practice:** +1. Before upgrading to a new 6.5.x service pack, review release notes for deprecation notices +2. Check compiler warnings for `@Deprecated` usage after upgrade +3. Test custom replication code against new service pack in lower environments +4. Monitor Adobe Experience League for announcements of API changes + +**Known Stable APIs (6.5.0 - 6.5.21+):** +- `Replicator` interface - Core methods stable across all 6.5.x versions +- `ReplicationOptions` - Configuration class stable +- `ReplicationStatus` - Status interface stable +- `AgentManager` - Agent management stable + +If your code uses only these documented public APIs from the `com.day.cq.replication` package, it should remain compatible across all AEM 6.5 LTS service packs. + +## Migration Considerations + +### AEM 6.5 LTS End of Life + +- **Extended Support Ends:** 2029 +- **Migration Target:** AEM as a Cloud Service +- **API Changes:** Replication API → Sling Distribution API + +### Code Future-Proofing + +**Avoid:** +- Agent ID filtering (tight coupling to agent names) +- Hardcoded agent paths +- Direct agent configuration manipulation + +**Prefer:** +- Agent-agnostic replication options +- Configuration-driven agent selection +- Abstraction layers over direct API calls + +This reduces migration effort when moving to Cloud Service. diff --git a/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/agent-types.md b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/agent-types.md new file mode 100644 index 0000000..06a0ebe --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/agent-types.md @@ -0,0 +1,181 @@ +# Replication Agent Types + +Reference guide for AEM 6.5 LTS replication agent types and their purposes. + +## Default Replication Agent (Author to Publish) + +**Purpose:** Distributes activated content from Author to Publish instances. + +**Key Characteristics:** +- Transport Type: HTTP/HTTPS +- Default URI Pattern: `http://publish-host:4503/bin/receive?sling:authRequestLogin=1` +- Serialization Type: Dispatcher Flush +- Direction: Author → Publish + +**When to Use:** +- Standard content publishing workflow +- Initial environment setup +- Multiple publish instances (one agent per instance) + +## Dispatcher Flush Agent + +**Purpose:** Invalidates cached content on Dispatcher after activation. + +**Key Characteristics:** +- Transport Type: HTTP/HTTPS +- Default URI Pattern: `http://dispatcher-host:80/dispatcher/invalidate.cache` +- Serialization Type: Dispatcher Flush +- Direction: Publish → Dispatcher + +**When to Use:** +- Every publish instance requires a flush agent +- Cache invalidation after content changes +- Coordinated with Author default agents + +**Dispatcher Configuration Required:** +```apache +/allowedClients { + /0 { /type "allow" /glob "*" } +} +``` + +## Reverse Replication Agent + +**Purpose:** Transfers user-generated content from Publish to Author. + +**Key Characteristics:** +- Transport Type: HTTP/HTTPS +- Reverse Direction: Publish → Author +- Outbox Path: `/var/replication/outbox` +- Direction: Publish → Author + +**When to Use:** +- Form submissions +- User-generated content +- Community features +- Social collaboration + +## Static Agent + +**Purpose:** Exports content to filesystem for static delivery. + +**Key Characteristics:** +- Transport Type: Filesystem +- Export Path: Local filesystem directory +- No network transport required + +**When to Use:** +- Static site generation +- Content export for external systems +- Filesystem-based CDN integration + +## Agent Configuration Common Fields + +All agent types share these configuration fields: + +- **Name:** Unique identifier for the agent +- **Enabled:** Boolean flag to activate/deactivate +- **Serialization Type:** Content format (Default, Dispatcher Flush, Static) +- **Retry Delay:** Milliseconds between retry attempts (default: 60000) +- **User ID:** Authentication user for target endpoint +- **Log Level:** Error, Info, Debug + +## Agent Ordering and Execution + +### Multiple Agents Processing Content + +When multiple agents are configured and enabled, replication behavior depends on agent filtering: + +**Without AgentFilter (default):** +- All enabled agents receive replication requests +- Agents process in **parallel** (not sequential) +- Replication completes when **all** agents finish successfully +- If **any** agent fails, the entire replication operation fails + +**With AgentFilter:** +- Only agents matching the filter receive requests +- Filtered agents process in parallel +- Completion/failure logic same as above + +### Ordering Implications + +**Agent Creation Order Does NOT Affect Processing:** +- Agents are not processed in creation order +- No guaranteed sequence between agents +- Each agent processes independently + +**Practical Impact:** +1. **Default Agents + Flush Agents:** Both process simultaneously when content activates +2. **Multiple Publish Agents:** All publish instances receive content in parallel (good for performance) +3. **Failure Propagation:** If publish1 succeeds but publish2 fails, the operation is marked as failed even though content reached publish1 + +**When Order Matters:** +Use `AgentFilter` to control which agents process specific requests: + +```java +// Example: Only activate to specific publish instance +ReplicationOptions opts = new ReplicationOptions(); +opts.setFilter(agent -> agent.getId().equals("publish_prod_1")); + +replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); +``` + +**Best Practice for High Availability:** +- Configure one agent per publish instance +- Let all agents process in parallel (maximize throughput) +- Monitor individual agent queues for failures +- Don't rely on agent ordering for correctness + +## Programmatic Agent Access + +When accessing agents programmatically via the AgentManager API, always validate agent availability: + +```java +import com.day.cq.replication.Agent; +import com.day.cq.replication.AgentManager; +import org.osgi.service.component.annotations.Reference; + +@Reference +private AgentManager agentManager; + +public void checkAgentStatus(String agentId) { + // Get agent with null check + Agent agent = agentManager.getAgent(agentId); + + if (agent == null) { + LOG.warn("Agent not found: {}", agentId); + return; + } + + // Validate agent configuration + if (!agent.isEnabled()) { + LOG.info("Agent {} is disabled", agentId); + return; + } + + // Check transport URI is configured + String transportURI = agent.getConfiguration().getTransportURI(); + if (transportURI == null || transportURI.trim().isEmpty()) { + LOG.error("Agent {} has no transport URI configured", agentId); + return; + } + + // Safe to use agent + LOG.info("Agent {} is enabled with URI: {}", agentId, transportURI); +} +``` + +**Key null safety checks:** +- Verify agent exists before accessing properties (`agent == null`) +- Check configuration fields before use (`transportURI == null`) +- Validate enabled status before operations +- Handle missing or malformed configuration gracefully + +## Best Practices + +1. **One Agent Per Target:** Create separate agents for each publish instance +2. **Service Users:** Use dedicated service accounts, never admin +3. **Monitoring:** Enable logging at Info level for production agents +4. **Naming Convention:** Use descriptive names like `publish_prod_1`, `flush_dispatcher_az1` +5. **Security:** Use HTTPS transport for external-facing endpoints +6. **Null Safety:** Always check agent existence and configuration validity when accessing programmatically diff --git a/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/api-reference.md b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/api-reference.md new file mode 100644 index 0000000..c2c041c --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/api-reference.md @@ -0,0 +1,411 @@ +# Replication API Quick Reference + +Quick reference for AEM 6.5 LTS Replication API methods and interfaces. + +## Core Interfaces + +### Replicator + +**Package:** `com.day.cq.replication.Replicator` + +**Purpose:** Core interface for replication operations + +| Method | Parameters | Returns | Use Case | +|--------|-----------|---------|----------| +| `replicate()` | Session, Type, String | void | Simple activation/deactivation | +| `replicate()` | Session, Type, String, Options | void | Activation with custom options | +| `replicate()` | Session, Type, String[], Options | void | Bulk activation of multiple paths | +| `checkPermission()` | Session, Type, String | void | Verify user can replicate (throws if denied) | +| `getReplicationStatus()` | Session, String | ReplicationStatus | Query replication state of resource | +| `getActivatedPaths()` | Session, String | Iterator | List all activated paths under root | + +### ReplicationOptions + +**Package:** `com.day.cq.replication.ReplicationOptions` + +**Purpose:** Configure replication behavior + +| Method | Parameter | Default | Use Case | +|--------|-----------|---------|----------| +| `setSynchronous()` | boolean | false | Wait for replication to complete | +| `setSuppressVersions()` | boolean | false | Don't create version on activation | +| `setSuppressStatusUpdate()` | boolean | false | Don't update replication metadata | +| `setFilter()` | AgentFilter | null | Target specific agents only | +| `setRevision()` | String | null | Replicate specific version | + +### ReplicationStatus + +**Package:** `com.day.cq.replication.ReplicationStatus` + +**Purpose:** Query replication state + +| Method | Returns | Description | +|--------|---------|-------------| +| `isActivated()` | boolean | True if replicated to at least one agent | +| `isDeactivated()` | boolean | True if deactivated | +| `getLastPublished()` | Calendar | Last activation timestamp | +| `getLastPublishedBy()` | String | User who last activated | +| `getLastReplicationAction()` | String | Last action (ACTIVATE, DEACTIVATE, etc.) | +| `getLastReplicatedRevision()` | String | Version ID last replicated | + +### AgentManager + +**Package:** `com.day.cq.replication.AgentManager` + +**Purpose:** Inspect and manage agents + +| Method | Parameters | Returns | Use Case | +|--------|-----------|---------|----------| +| `getAgents()` | - | Map | Get all agents | +| `getAgents()` | AgentFilter | Map | Get filtered agents | +| `getAgent()` | String agentId | Agent | Get specific agent | + +### ReplicationQueue + +**Package:** `com.day.cq.replication.ReplicationQueue` + +**Purpose:** Inspect queue state (read-only via JMX) + +| Method | Returns | Description | +|--------|---------|-------------| +| `getEntries()` | Collection | All queued items | +| `getSize()` | int | Queue depth | +| `getState()` | State | IDLE, RUNNING, BLOCKED, PAUSED | + +### ReplicationListener + +**Package:** `com.day.cq.replication.ReplicationListener` + +**Purpose:** Monitor replication events + +| Method | Parameters | Use Case | +|--------|-----------|----------| +| `onStart()` | Agent, ReplicationAction | Replication initiated | +| `onEnd()` | Agent, ReplicationAction, ReplicationResult | Replication completed | +| `onError()` | Agent, ReplicationAction, Exception | Replication failed | + +## ReplicationActionType Enum + +**Package:** `com.day.cq.replication.ReplicationActionType` + +| Constant | Purpose | +|----------|---------| +| `ACTIVATE` | Publish content to target instances | +| `DEACTIVATE` | Remove content from target instances | +| `DELETE` | Delete content on target (use with caution) | +| `TEST` | Test agent connectivity without replicating | +| `INTERNAL_POLL` | Internal use (reverse replication) | + +## Exception Handling + +### ReplicationException + +**Package:** `com.day.cq.replication.ReplicationException` + +**Common Causes:** +- Network connectivity failure +- Authentication error +- Target instance unavailable +- Invalid resource path +- Insufficient permissions + +**Handling Pattern:** +```java +try { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); +} catch (ReplicationException e) { + LOG.error("Replication failed for path: {}", path, e); + // Handle error (retry, alert, log) +} +``` + +## Common Patterns + +### Simple Activation + +```java +@Reference +private Replicator replicator; + +public void activate(ResourceResolver resolver, String path) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); +} +``` + +### Synchronous Activation + +```java +public void activateSync(ResourceResolver resolver, String path) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); +} +``` + +### Bulk Activation + +```java +public void activateBulk(ResourceResolver resolver, List paths) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(false); // Async for performance + + String[] pathArray = paths.toArray(new String[0]); + replicator.replicate(session, ReplicationActionType.ACTIVATE, pathArray, opts); +} +``` + +### Agent Filtering + +```java +public void activateToSpecificAgent(ResourceResolver resolver, String path, + String targetAgentId) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setFilter(agent -> agent.getId().equals(targetAgentId)); + + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); +} +``` + +### Check Replication Status + +```java +public boolean isActivated(ResourceResolver resolver, String path) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + return false; + } + + ReplicationStatus status = replicator.getReplicationStatus(session, path); + return status != null && status.isActivated(); +} +``` + +### Permission Check + +```java +public boolean canActivate(ResourceResolver resolver, String path) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + return false; + } + + try { + replicator.checkPermission(session, ReplicationActionType.ACTIVATE, path); + return true; + } catch (ReplicationException e) { + return false; // User lacks permission + } +} +``` + +## Service User Pattern + +```java +@Reference +private ResourceResolverFactory resolverFactory; + +@Reference +private Replicator replicator; + +public void activateWithServiceUser(String path) throws ReplicationException { + Map authInfo = new HashMap<>(); + authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); + + try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo)) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + } catch (LoginException e) { + LOG.error("Service user login failed", e); + throw new ReplicationException("Authentication failed", e); + } +} +``` + +## Error Recovery Patterns + +### Retry with Exponential Backoff + +Retry transient failures with increasing delays to avoid overwhelming the target system. + +```java +public void activateWithRetry(ResourceResolver resolver, String path, int maxRetries) + throws ReplicationException { + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + int attempt = 0; + long delayMs = 1000; // Start with 1 second + + while (attempt < maxRetries) { + try { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + LOG.info("Replication succeeded on attempt {}", attempt + 1); + return; // Success + + } catch (ReplicationException e) { + attempt++; + + if (attempt >= maxRetries) { + LOG.error("Replication failed after {} attempts", maxRetries, e); + throw e; // Give up after max retries + } + + // Exponential backoff: 1s, 2s, 4s, 8s, etc. + LOG.warn("Replication attempt {} failed, retrying in {}ms", attempt, delayMs, e); + + try { + Thread.sleep(delayMs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new ReplicationException("Retry interrupted", ie); + } + + delayMs *= 2; // Double the delay for next attempt + } + } +} +``` + +### Fallback to Asynchronous on Timeout + +If synchronous replication times out, fall back to asynchronous mode. + +```java +public void activateWithFallback(ResourceResolver resolver, String path) + throws ReplicationException { + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + // Try synchronous first + ReplicationOptions syncOpts = new ReplicationOptions(); + syncOpts.setSynchronous(true); + + try { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, syncOpts); + LOG.info("Synchronous replication completed"); + + } catch (ReplicationException e) { + // Check if it's a timeout or transient error + if (isTransientError(e)) { + LOG.warn("Synchronous replication failed, falling back to async", e); + + // Fallback to asynchronous + ReplicationOptions asyncOpts = new ReplicationOptions(); + asyncOpts.setSynchronous(false); + + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, asyncOpts); + LOG.info("Asynchronous replication queued as fallback"); + } else { + // Permanent error, don't retry + throw e; + } + } +} + +private boolean isTransientError(ReplicationException e) { + String message = e.getMessage(); + return message != null && ( + message.contains("timeout") || + message.contains("Connection refused") || + message.contains("SocketTimeoutException") + ); +} +``` + +### Circuit Breaker Pattern + +Prevent cascading failures by stopping replication attempts when the target system is down. + +```java +public class ReplicationCircuitBreaker { + + private static final int FAILURE_THRESHOLD = 5; + private static final long TIMEOUT_MS = 60000; // 1 minute + + private int failureCount = 0; + private long lastFailureTime = 0; + private boolean circuitOpen = false; + + @Reference + private Replicator replicator; + + public void activateWithCircuitBreaker(ResourceResolver resolver, String path) + throws ReplicationException { + + // Check if circuit is open + if (circuitOpen) { + if (System.currentTimeMillis() - lastFailureTime < TIMEOUT_MS) { + throw new ReplicationException("Circuit breaker open - replication disabled temporarily"); + } else { + // Timeout expired, try to close circuit + circuitOpen = false; + failureCount = 0; + LOG.info("Circuit breaker reset, attempting replication"); + } + } + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt to Session"); + } + + try { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + + // Success - reset failure count + failureCount = 0; + LOG.info("Replication successful"); + + } catch (ReplicationException e) { + failureCount++; + lastFailureTime = System.currentTimeMillis(); + + if (failureCount >= FAILURE_THRESHOLD) { + circuitOpen = true; + LOG.error("Circuit breaker opened after {} failures", failureCount); + } + + throw e; + } + } +} +``` + +## Official Documentation + +- **JavaDoc:** https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html +- **User Guide:** https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication +- **Troubleshooting:** https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep diff --git a/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/queue-mechanics.md b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/queue-mechanics.md new file mode 100644 index 0000000..ed26060 --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/references/replication-foundation/queue-mechanics.md @@ -0,0 +1,278 @@ +# Replication Queue Mechanics + +Reference guide for AEM 6.5 LTS replication queue behavior and management. + +## Queue Architecture + +### FIFO Processing + +Replication queues use **First-In-First-Out (FIFO)** ordering: +- Replication requests processed in submission order +- No priority-based reordering +- Blocking items prevent queue progress + +### Queue States + +**Active Queue:** +- Items currently being processed or waiting +- Visible in Agent UI: `/etc/replication/agents.author/.html` +- JMX Bean: `com.day.cq.replication:type=Agent,id=` + +**Blocked Queue:** +- Queue processing stopped due to: + - Network connectivity failure + - Target instance unavailable + - Authentication failure + - Serialization error +- Requires manual intervention or automatic retry + +## Queue Item Lifecycle + +``` +Submit → Queue → Serialize → Transport → Acknowledge → Remove + ↓ ↓ + (pending) (blocked?) + ↓ + Retry Logic +``` + +### States + +1. **Pending:** Waiting in queue for processing +2. **Active:** Currently being serialized and transported +3. **Blocked:** Failed, awaiting retry +4. **Completed:** Successfully replicated and removed from queue + +## Retry Logic + +### Automatic Retry + +- **Retry Delay:** Configured per agent (default: 60000ms = 1 minute) +- **Maximum Retries:** Infinite until manually cleared +- **Exponential Backoff:** Not implemented - fixed retry delay + +### Retry Triggers + +Replication retries on: +- Network timeout +- HTTP 5xx server errors +- Connection refused + +Does NOT retry on: +- HTTP 4xx client errors (permanent failure) +- Invalid credentials (401/403) +- Missing resources (404) + +## Queue Management + +### Via UI + +Navigate to: `/etc/replication/agents.author/.html` + +Actions: +- **View Queue:** See pending items +- **Retry All:** Re-attempt all blocked items +- **Clear Queue:** Remove all items (destructive) +- **Refresh:** Update queue display + +### Via JMX Console + +Navigate to: `http://localhost:4502/system/console/jmx` + +MBean: `com.day.cq.replication:type=Agent,id=` + +Metrics: +- `QueueNumEntries` - Current queue depth +- `QueueBlocked` - Boolean blocked status +- `QueueProcessingSince` - Timestamp of current item + +Operations: +- `retryFirst()` - Retry first blocked item +- `clear()` - Empty queue (destructive) + +**JMX Security Considerations:** + +Access to JMX operations requires appropriate permissions: + +- **Authentication:** JMX console requires administrator credentials (`http://localhost:4502/system/console/jmx`) +- **Authorization:** Only users in the `administrators` group can invoke JMX operations by default +- **Audit Logging:** JMX operations are not logged in `replication.log` - enable JMX audit logging for compliance environments +- **Production Access:** Restrict JMX console access in production: + - Use firewall rules to block external access to JMX ports + - Require VPN or SSH tunnel for remote JMX access + - Consider read-only JMX access for monitoring users (metrics only, no operations) +- **Destructive Operations:** `clear()` permanently removes queued replication items - use with caution and only after verifying queue state + +**Recommended Production Setup:** +- Monitoring tools (New Relic, Datadog): Read-only JMX access for metrics +- Operations team: Full JMX access via secure channel (VPN/SSH) +- Automated scripts: Use HTTP-based Sling APIs instead of JMX where possible + +## Performance Considerations + +### Queue Depth Thresholds + +- **Normal:** 0-10 items +- **Elevated:** 10-50 items (investigate slowness) +- **Critical:** 50+ items (likely blocked or overloaded) + +### Queue Saturation + +High queue depth causes: +- Delayed content publication +- Increased memory usage (serialized content cached) +- Potential replication event backlog + +**Mitigation:** +- Monitor queue metrics via JMX +- Use asynchronous replication for bulk operations +- Increase target instance capacity +- Split workload across multiple agents + +## Blocking Behavior + +### FIFO Blocking + +If item N blocks: +- Items N+1, N+2, ... cannot process +- Entire queue waits for item N to succeed or be cleared +- No automatic skip of blocked items + +**Resolution:** +1. Fix root cause (network, credentials, target instance) +2. Retry blocked item +3. Or clear blocked item manually + +### Preventing Blocks + +- Test agent connectivity before activation +- Monitor agent health via JMX +- Use HTTP connection pooling +- Configure appropriate timeouts +- Validate target instance availability + +## Common Queue Issues + +### Queue Never Empties + +**Symptom:** Queue depth stays constant or grows + +**Causes:** +- Agent disabled +- Target instance down +- Network partition +- Authentication failure + +**Diagnosis:** +1. Check agent enabled status +2. Test target URI with curl +3. Review agent error.log +4. Check JMX QueueBlocked status + +### Queue Processes Slowly + +**Symptom:** Queue depth gradually increases + +**Causes:** +- Target instance CPU/memory constrained +- Network latency +- Large asset replication +- Inefficient serialization + +**Diagnosis:** +1. Monitor target instance resource usage +2. Check network latency (ping, traceroute) +3. Review replication.log for timing +4. Consider asynchronous replication + +## Queue Listener Patterns + +For event-driven queue monitoring, implement a custom `ReplicationListener` to react to queue state changes: + +```java +import com.day.cq.replication.Agent; +import com.day.cq.replication.ReplicationAction; +import com.day.cq.replication.ReplicationListener; +import com.day.cq.replication.ReplicationResult; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component( + service = ReplicationListener.class, + immediate = true +) +public class QueueMonitorListener implements ReplicationListener { + + private static final Logger LOG = LoggerFactory.getLogger(QueueMonitorListener.class); + private static final int ALERT_THRESHOLD = 20; + + @Override + public void onStart(Agent agent, ReplicationAction action) { + // Check queue depth at replication start + int queueSize = agent.getQueue().getEntries().size(); + + if (queueSize > ALERT_THRESHOLD) { + LOG.warn("Queue depth elevated for agent {}: {} items", + agent.getId(), queueSize); + // Send alert to monitoring system + alertHighQueueDepth(agent.getId(), queueSize); + } + } + + @Override + public void onEnd(Agent agent, ReplicationAction action, ReplicationResult result) { + if (result.isSuccess()) { + LOG.debug("Replication completed for path: {}", action.getPath()); + } else { + LOG.error("Replication failed for path: {}, Queue may be blocked", + action.getPath()); + checkQueueBlocked(agent); + } + } + + @Override + public void onError(Agent agent, ReplicationAction action, Exception exception) { + LOG.error("Replication error for path: {}", action.getPath(), exception); + + // Check if queue is blocked + if (agent.getQueue().getState() == ReplicationQueue.State.BLOCKED) { + LOG.error("Queue BLOCKED for agent: {}", agent.getId()); + alertQueueBlocked(agent.getId()); + } + } + + private void checkQueueBlocked(Agent agent) { + if (agent.getQueue().getState() == ReplicationQueue.State.BLOCKED) { + alertQueueBlocked(agent.getId()); + } + } + + private void alertHighQueueDepth(String agentId, int queueSize) { + // Integration with monitoring system (e.g., Datadog, New Relic, PagerDuty) + } + + private void alertQueueBlocked(String agentId) { + // Send critical alert - queue is blocked and requires intervention + } +} +``` + +**Event-Driven Monitoring Use Cases:** +- **Alerting:** Trigger PagerDuty/Slack alerts when queue depth exceeds threshold +- **Metrics Collection:** Send queue depth metrics to Datadog/New Relic +- **Auto-Remediation:** Trigger automated retry or clear stale items +- **Audit Logging:** Record all replication events for compliance + +**Listener Registration:** +Listeners registered as OSGi services are automatically invoked by the replication framework for all replication events across all agents. + +## Best Practices + +1. **Monitor Queue Depth:** Alert on sustained depth > 20 items +2. **Regular Health Checks:** Test agent connectivity daily +3. **Async for Bulk:** Use asynchronous replication for large batches +4. **Clear Stale Items:** Remove failed items that won't retry successfully +5. **Size Appropriately:** One agent per publish instance, no sharing +6. **Log Monitoring:** Watch for repeated retry patterns indicating systemic issues +7. **Event-Driven Monitoring:** Implement ReplicationListener for real-time queue monitoring and alerting diff --git a/skills/aem/6.5-lts/skills/aem-replication/replicate-content/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/replicate-content/SKILL.md new file mode 100644 index 0000000..900540e --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/replicate-content/SKILL.md @@ -0,0 +1,838 @@ +--- +name: replicate-content +description: Activate and deactivate content in AEM 6.5 LTS using replication agents for publishing to target instances +--- + +# Replicate Content in AEM 6.5 LTS + +This skill guides you through activating (publishing) and deactivating (unpublishing) content in Adobe Experience Manager 6.5 LTS. Replication moves content from the Author environment to one or more Publish instances, making it available to end users. + +## When to Use This Skill + +Use this skill when you need to: +- Publish pages from Author to Publish environment +- Activate modified content to make changes live +- Deactivate (unpublish) content to remove it from Publish +- Perform tree activation for entire site sections +- Use programmatic replication via API +- Trigger replication via workflows +- Schedule content activation/deactivation +- Replicate assets and DAM content +- Perform package-based replication + +## Prerequisites + +- AEM 6.5 LTS Author instance with configured replication agents +- At least one replication agent enabled and working (see `configure-replication-agent` skill) +- Author permissions to activate/deactivate content +- Content created and ready for publication +- Understanding of content approval workflows (if implemented) + +## Content Replication Methods + +AEM 6.5 LTS provides seven primary methods for triggering replication: + +| Method | Use Case | Scope | Access | +|--------|----------|-------|--------| +| **Quick Publish** | Simple page activation | Single page (shallow) | Page/Sites console | +| **Manage Publication** | Advanced control | Single/multiple pages | Page/Sites console | +| **Tree Activation** | Hierarchical publishing | Page tree | Classic UI Tools | +| **Package Manager** | Specific content sets | Custom packages | Tools console | +| **Workflows** | Approval-based | Any content | Workflow console | +| **Scheduled Activation** | Time-based | Single/multiple pages | Page properties | +| **Replication API** | Programmatic | Any resource | Custom code | + +## Method 1: Quick Publish (Recommended for Simple Cases) + +Quick Publish provides the fastest way to activate content with minimal clicks. + +### Steps: + +1. **Navigate to Sites console:** + ``` + http://localhost:4502/sites.html/content + ``` + +2. **Select content:** + - Click checkbox next to page(s) to publish + - Multiple selection supported + +3. **Click "Quick Publish":** + - Button appears in top toolbar + - Alternatively: Right-click → Quick Publish + +4. **Confirmation:** + - Green success message appears + - Page status shows "Published" with green checkmark + - Replication timestamp updates + +### Characteristics: +- **Shallow replication**: Only selected page(s), not child pages +- **Immediate**: No delay or scheduling +- **Default agents**: Uses all enabled default replication agents +- **No preview**: Content publishes immediately without review + +### When to use Quick Publish: +- Simple page updates +- Single page activation +- No approval workflow needed +- Immediate publication required + +## Method 2: Manage Publication (Advanced Control) + +Manage Publication wizard provides granular control over what, when, and how content is published. + +### Steps: + +1. **Select content in Sites console:** + ``` + Select page(s) → Click "Manage Publication" in toolbar + ``` + +2. **Choose action:** + - **Publish**: Activate to Publish instances + - **Unpublish**: Deactivate from Publish instances + - **Publish Later**: Schedule future activation + - **Unpublish Later**: Schedule future deactivation + +3. **Configure scheduling (if "Later" selected):** + ``` + Activation Date: [YYYY-MM-DD] + Activation Time: [HH:MM] + ``` + +4. **Select scope:** + - **Selected**: Only chosen pages + - **Modified**: Pages modified since last publication + - **Published**: Already published pages + - **Include children**: Replicate child pages (tree activation) + +5. **Configure options:** + - **Create version**: Snapshot before publish + - **Workflow**: Trigger approval workflow + - **References**: Include referenced assets/pages + - **Activate modified pages only**: Skip unchanged content + +6. **Select workflow (if applicable):** + ``` + Workflow Model: [Request for Activation / Approval Workflow / Custom] + Workflow Title: "Publish Homepage Updates" + ``` + +7. **Review and confirm:** + - Summary shows: + - Pages to publish + - Schedule details + - Workflow assignment + - Click "Publish" or "Next" to proceed + +8. **Monitor progress:** + - Progress bar shows replication status + - Success/failure notifications appear + +### Advanced Options: + +**Include References:** +- Automatically publishes linked assets +- Includes DAM images, documents +- Publishes referenced experience fragments +- Ensures no broken links on Publish + +**Create Version Before Publish:** +- Creates snapshot in version history +- Enables rollback if needed +- Recommended for major updates + +**Only Modified Pages:** +- Skips pages unchanged since last activation +- Improves performance for large trees +- Reduces replication queue load + +### When to use Manage Publication: +- Publishing multiple pages with dependencies +- Scheduled activation/deactivation needed +- Approval workflow required +- Tree activation with selective inclusion +- Creating version snapshots before publish + +## Method 3: Tree Activation (Hierarchical Publishing) + +Tree Activation publishes an entire content tree hierarchy in one operation. + +### Steps (Classic UI): + +1. **Navigate to Tools console:** + ``` + http://localhost:4502/miscadmin + ``` + +2. **Access Replication tools:** + - Click "Replication" in left sidebar + - Select "Activate Tree" + +3. **Configure tree activation:** + ``` + Start Path: /content/mysite/en + + Options: + - Only Modified: ✓ (publish only changed pages) + - Only Activated: ✓ (publish only previously activated) + - Ignore Deactivated: ✓ (skip deactivated pages) + + Dry Run: ✓ (test without actually publishing) + ``` + +4. **Execute or dry run:** + - **Dry Run**: Preview what will be published + - **Activate**: Execute actual replication + - Progress indicator shows page count + +5. **Review results:** + - Success count + - Failed pages (if any) + - Log details for troubleshooting + +### When to use Tree Activation: +- Publishing entire site sections +- Initial site launch +- Large content migrations +- Bulk replication operations + +### Performance considerations: +- Can generate large replication queues +- May take significant time for large trees +- Use "Only Modified" to reduce load +- Consider off-peak hours for large activations + +## Method 4: Package-Based Replication + +Package Manager allows replicating specific content sets across environments. + +### Steps: + +1. **Create package:** + ``` + Navigate to: http://localhost:4502/crx/packmgr/index.jsp + Click "Create Package" + + Package Name: content-update-2024-04 + Group: myproject + Version: 1.0.0 + ``` + +2. **Define package filters:** + ``` + Click "Edit" → "Filters" tab + + Add filter(s): + Root Path: /content/mysite/en/products + Rules: Include (default) + + Add another: + Root Path: /content/dam/mysite/images/products + Rules: Include + ``` + +3. **Build package:** + ``` + Click "Build" + Wait for completion + Package .zip created + ``` + +4. **Replicate package:** + ``` + Click "More" → "Replicate" + Package sent to replication queue + Distributed to all enabled Publish instances + ``` + +5. **Verify on Publish:** + ``` + Navigate to Publish CRX Package Manager: + http://publish:4503/crx/packmgr/index.jsp + + Package should appear in list + Status: Installed + ``` + +### When to use Package Replication: +- Complex content sets with dependencies +- Cross-environment deployments +- Backup and restore operations +- Specific node structure replication +- Configuration replication + +## Method 5: Workflow-Based Replication + +Workflows enable approval processes before content goes live. + +### Built-in Workflow: Request for Activation + +1. **Trigger workflow:** + ``` + Select page(s) → Timeline panel (left sidebar) + Click "Start Workflow" + ``` + +2. **Select workflow model:** + ``` + Workflow: Request for Activation + Title: "Homepage Redesign Approval" + Comment: "Please review updated homepage design" + ``` + +3. **Assign approver:** + ``` + Participant: [User or Group] + Example: content-approvers + ``` + +4. **Submit request:** + ``` + Click "Create" + Workflow initiates + Notification sent to approver + ``` + +5. **Approver actions:** + ``` + Approver receives inbox notification: + http://localhost:4502/aem/inbox + + Options: + - Approve: Content activates to Publish + - Reject: Returns to author with comments + - Request changes: Sends back for revisions + ``` + +6. **After approval:** + - Content automatically replicates + - Workflow completes + - Status updates to "Published" + +### Custom Workflow Models + +Create custom workflows for complex approval chains: + +``` +Navigate to: Tools → Workflow → Models + +Example workflow steps: +1. Content Author creates/modifies +2. → Content Lead reviews +3. → Legal compliance check +4. → Final approval +5. → Automatic activation +6. → Notification to stakeholders +``` + +## Method 6: Scheduled Activation/Deactivation + +Schedule content to publish or unpublish at specific date/time. + +### Configure via Page Properties: + +1. **Open page properties:** + ``` + Select page → Properties (toolbar or right-click) + ``` + +2. **Navigate to Advanced tab:** + ``` + Click "Advanced" in left sidebar + ``` + +3. **Set On Time (activation):** + ``` + On Time: 2024-04-15 09:00:00 + + Effect: Page automatically activates at specified time + Requires: Replication agent with "On-/Offtime reached" trigger enabled + ``` + +4. **Set Off Time (deactivation):** + ``` + Off Time: 2024-05-15 23:59:00 + + Effect: Page automatically deactivates at specified time + Removes from Publish instances + ``` + +5. **Save properties:** + ``` + Click "Save & Close" + ``` + +### Verify Scheduled Activation: + +``` +Replication agent configuration: +Navigate to: /etc/replication/agents.author/[agent-name] +Triggers tab: "On-/Offtime reached" must be enabled +``` + +### When to use Scheduled Activation: +- Marketing campaigns with specific launch dates +- Time-sensitive announcements +- Content embargoes +- Temporary promotions +- Event-based content + +## Method 7: Programmatic Replication (API) + +Use the Replication API for custom code integration. + +### Java API Example: + +```java +import com.day.cq.replication.Replicator; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import org.apache.sling.api.resource.ResourceResolver; + +@Reference +private Replicator replicator; + +public void activatePage(ResourceResolver resolver, String pagePath) + throws ReplicationException { + + // Activate (publish) content + replicator.replicate( + resolver.adaptTo(Session.class), + ReplicationActionType.ACTIVATE, + pagePath + ); +} + +public void deactivatePage(ResourceResolver resolver, String pagePath) + throws ReplicationException { + + // Deactivate (unpublish) content + replicator.replicate( + resolver.adaptTo(Session.class), + ReplicationActionType.DEACTIVATE, + pagePath + ); +} + +public void deleteFromPublish(ResourceResolver resolver, String pagePath) + throws ReplicationException { + + // Delete content from Publish + replicator.replicate( + resolver.adaptTo(Session.class), + ReplicationActionType.DELETE, + pagePath + ); +} +``` + +### ReplicationActionType Options: + +| Type | Purpose | Effect | +|------|---------|--------| +| `ACTIVATE` | Publish content | Sends to Publish instances | +| `DEACTIVATE` | Unpublish content | Removes from Publish | +| `DELETE` | Delete from Publish | Permanent removal | +| `TEST` | Test replication | Verifies connectivity | +| `REVERSE` | Reverse replicate | Publish → Author | + +### Advanced API: ReplicationOptions + +```java +import com.day.cq.replication.ReplicationOptions; +import com.day.cq.replication.AgentFilter; + +// Custom replication with options +ReplicationOptions opts = new ReplicationOptions(); + +// Synchronous replication (wait for completion) +opts.setSynchronous(true); + +// Suppress versions +opts.setSuppressVersions(true); + +// Filter to specific agents +opts.setFilter(new AgentFilter() { + public boolean isIncluded(Agent agent) { + return agent.getId().equals("publish_instance_1"); + } +}); + +// Execute with options +replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath, opts); +``` + +### CURL API Examples + +**Activate page (with error handling):** +```bash +# Note: Replace $AEM_USER:$AEM_PASSWORD with your service account credentials +response=$(curl -s -w "\n%{http_code}" -u $AEM_USER:$AEM_PASSWORD -X POST \ + http://localhost:4502/bin/replicate.json \ + -F "cmd=Activate" \ + -F "path=/content/mysite/en/products") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" -eq 200 ]; then + # Validate JSON response structure and success status + if command -v jq &> /dev/null; then + success=$(echo "$body" | jq -r '.success // empty') + path=$(echo "$body" | jq -r '.path // empty') + + if [ "$success" = "true" ]; then + echo "Activation successful for path: $path" + else + error=$(echo "$body" | jq -r '.message // "Unknown error"') + echo "Activation reported failure: $error" + exit 1 + fi + else + echo "Activation successful (HTTP 200)" + echo "$body" + fi +else + echo "Activation failed with HTTP $http_code" + echo "$body" + exit 1 +fi +``` + +**Deactivate page (with error handling):** +```bash +# Note: Replace $AEM_USER:$AEM_PASSWORD with your service account credentials +response=$(curl -s -w "\n%{http_code}" -u $AEM_USER:$AEM_PASSWORD -X POST \ + http://localhost:4502/bin/replicate.json \ + -F "cmd=Deactivate" \ + -F "path=/content/mysite/en/products") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" -eq 200 ]; then + # Validate JSON response structure and success status + if command -v jq &> /dev/null; then + success=$(echo "$body" | jq -r '.success // empty') + path=$(echo "$body" | jq -r '.path // empty') + + if [ "$success" = "true" ]; then + echo "Deactivation successful for path: $path" + else + error=$(echo "$body" | jq -r '.message // "Unknown error"') + echo "Deactivation reported failure: $error" + exit 1 + fi + else + echo "Deactivation successful (HTTP 200)" + echo "$body" + fi +else + echo "Deactivation failed with HTTP $http_code" + echo "$body" + exit 1 +fi +``` + +**Tree activation via CURL (with error handling):** +```bash +# Note: Replace $AEM_USER:$AEM_PASSWORD with your service account credentials +response=$(curl -s -w "\n%{http_code}" -u $AEM_USER:$AEM_PASSWORD -X POST \ + http://localhost:4502/etc/replication/treeactivation.html \ + -F "path=/content/mysite/en" \ + -F "onlyModified=true") + +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') + +if [ "$http_code" -eq 200 ]; then + # Note: Tree activation returns HTML, not JSON + # Check for success indicators in HTML response + if echo "$body" | grep -q "successfully"; then + echo "Tree activation initiated successfully" + # Extract and display any useful information from the HTML response + if command -v jq &> /dev/null && echo "$body" | jq empty 2>/dev/null; then + # If response is JSON (rare but possible) + status=$(echo "$body" | jq -r '.status // "initiated"') + echo "Status: $status" + fi + else + echo "Tree activation may have encountered issues. Check response:" + echo "$body" + fi +else + echo "Tree activation failed with HTTP $http_code" + echo "$body" + exit 1 +fi +``` + +**Simple examples (without error handling):** + +For quick testing, you can use simpler commands: +```bash +# Quick activate (no error handling) +curl -u $AEM_USER:$AEM_PASSWORD -X POST \ + http://localhost:4502/bin/replicate.json \ + -F "cmd=Activate" \ + -F "path=/content/mysite/en/products" + +# Quick deactivate (no error handling) +curl -u $AEM_USER:$AEM_PASSWORD -X POST \ + http://localhost:4502/bin/replicate.json \ + -F "cmd=Deactivate" \ + -F "path=/content/mysite/en/products" +``` + +Note: Simple examples above don't check for errors - use error handling versions in production scripts. + +## Asset Replication (DAM) + +Replicate digital assets from Author to Publish. + +### Steps: + +1. **Navigate to Assets console:** + ``` + http://localhost:4502/assets.html/content/dam + ``` + +2. **Select asset(s):** + - Click checkbox on images, videos, documents + - Multiple selection supported + +3. **Click "Quick Publish" or "Manage Publication":** + - Same options as page replication + - Assets replicate to Publish DAM + +4. **Verify asset renditions:** + - Original asset replicates + - All renditions replicate (thumbnails, web-optimized, etc.) + - Metadata replicates + +### Asset Replication Considerations: + +**Large file handling:** +- Binary-less replication available for shared datastore +- Direct binary access reduces replication overhead +- Configure in replication agent: Serialization Type = Binary-less + +**Asset dependencies:** +- Replicate referenced assets when publishing pages +- Use "Include References" in Manage Publication +- Avoids broken image links on Publish + +## Deactivation (Unpublishing) + +Remove content from Publish instances. + +### Quick Unpublish: + +``` +Sites console → Select page(s) → Quick Unpublish +``` + +### Manage Publication (Unpublish): + +``` +Sites console → Select page(s) → Manage Publication +Action: Unpublish +Scope: Selected / Include children +Click "Unpublish" +``` + +### Effect of Deactivation: +- Content removed from Publish instances +- URLs return 404 on Publish +- Content remains on Author (not deleted) +- Can be re-activated later +- Dispatcher cache invalidated + +## Monitoring Replication Status + +### Check Page Replication Status: + +**Sites console:** +- Green checkmark = Published +- Gray circle = Never published +- Orange dot = Modified since last publish +- Timeline shows replication history + +**Page Properties:** +- Click page → Properties → Basic tab +- Shows: Last published, Last published by, Last modified + +### Replication Queue Status: + +``` +Navigate to: Tools → Deployment → Replication +Click: Agents on author +Select: [agent name] + +View: +- Queue status (idle/active/blocked) +- Pending items count +- Last replication timestamp +``` + +### Replication Logs: + +``` +Navigate to: Tools → Operations → Logs + +Log file: replication.log +Location: crx-quickstart/logs/replication.log + +Typical entries: +- Replication initiated: path, type, user +- Replication succeeded/failed: status, duration +- Queue events: item added, processed, failed +``` + +## Troubleshooting Replication + +### Content Not Appearing on Publish + +**Check replication status:** +1. Verify page shows "Published" in Sites console +2. Check replication timestamp is recent +3. Review replication agent queue (should be empty after processing) + +**Verify on Publish directly:** +``` +http://publish-host:4503/content/mysite/en/page-name.html +``` + +**Check Dispatcher cache:** +- May serve cached old version +- Wait for cache invalidation +- Or manually flush cache +- Verify Dispatcher Flush agent is enabled and working + +### Replication Fails + +**Common causes:** + +1. **Replication agent blocked:** + - Check agent status (should be green) + - Review queue for failed items + - Click "Force Retry" or "Clear" failed item + +2. **Publish instance unavailable:** + - Verify Publish is running: `http://publish:4503/system/console` + - Test agent connection + - Check network connectivity + +3. **Insufficient permissions:** + - Verify Agent User Id has replication privileges + - Check user permissions on target content paths + - Review error logs for permission denied messages + +4. **Content locked:** + - Check if page is locked by another user + - Unlock page: Page → Unlock + - Retry replication + +### Page Modified After Publication + +If page shows orange dot (modified since publish): + +``` +Option 1: Re-publish changes +Select page → Quick Publish + +Option 2: Review changes first +Select page → Manage Publication +Check "Modified" scope to see what changed +Proceed with publication +``` + +## Best Practices + +1. **Use appropriate method:** + - Quick Publish for simple single-page updates + - Manage Publication for complex scenarios + - Tree Activation for entire sections + - Workflows for approval processes + +2. **Verify before publishing:** + - Preview content in Author + - Use staging/UAT environment first + - Test with limited user group before full launch + +3. **Monitor replication:** + - Check replication status after activation + - Verify content appears correctly on Publish + - Review logs for errors + +4. **Schedule large activations:** + - Use off-peak hours for tree activations + - Avoid business hours for major content releases + - Coordinate with operations team + +5. **Handle dependencies:** + - Always publish referenced assets + - Use "Include References" option + - Verify asset paths are correct + +6. **Use workflows for sensitive content:** + - Implement approval for homepage, legal pages + - Multi-stage review for compliance + - Audit trail via workflow history + +7. **Clean up deactivated content:** + - Regularly review unpublished pages + - Delete obsolete content from Author + - Maintain organized content tree + +8. **Test replication agents:** + - Periodically test agent connectivity + - Verify multiple Publish instances receiving content + - Monitor agent performance metrics + +## Security Considerations + +**Replication permissions:** +- Grant minimum required activation rights +- Use groups for permission management +- Audit replication activity via logs + +**Sensitive content:** +- Use workflows for approval +- Restrict activation to authorized users +- Implement content security policies + +**API usage:** +- Secure service user accounts for programmatic replication +- Never hardcode credentials +- Use resource resolver with appropriate permissions + +## Related Skills + +- `configure-replication-agent`: Set up and configure replication agents +- `troubleshoot-replication`: Diagnose and fix replication issues +- For AEM as a Cloud Service, see content distribution documentation (different architecture) + +## Success Criteria + +- ✓ Content successfully published to Publish instance(s) +- ✓ Page status shows "Published" with green checkmark +- ✓ Content visible on Publish URL +- ✓ Replication agent queue processed without errors +- ✓ Referenced assets also published +- ✓ Dispatcher cache invalidated (if applicable) +- ✓ Scheduled activation configured correctly (if used) +- ✓ Workflow approval completed (if required) +- ✓ Deactivation removes content from Publish (if tested) +- ✓ Replication logs show successful entries + +## Additional Resources + +- [Official AEM 6.5 LTS Replication Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication) +- [AEM 6.5 LTS Replication Troubleshooting Guide](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep) +- [AEM 6.5 LTS Documentation Hub](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts) +- [Replication API JavaDoc (Package Summary)](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) +- [Managing Publications Documentation](https://experienceleague.adobe.com/docs/experience-manager-65/authoring/authoring/publishing-pages.html) +- Workflow Models documentation for approval processes diff --git a/skills/aem/6.5-lts/skills/aem-replication/replication-api/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/replication-api/SKILL.md new file mode 100644 index 0000000..f6f8e1c --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/replication-api/SKILL.md @@ -0,0 +1,1892 @@ +--- +name: replication-api +description: Use the AEM 6.5 LTS Replication API for programmatic content activation, deactivation, and replication status management +--- + +# AEM 6.5 LTS Replication API + +This skill provides comprehensive guidance on using the official Adobe Experience Manager 6.5 LTS Replication API for programmatic replication operations. The API enables custom code to activate, deactivate, and manage content distribution workflows. + +## When to Use This Skill + +Use this skill when you need to: +- Programmatically activate/deactivate content from custom code +- Build custom replication workflows in OSGi services or servlets +- Integrate replication with external systems +- Implement custom replication triggers and automation +- Check replication status in application logic +- Create custom content distribution tools +- Bulk replicate content via scripts +- Implement conditional replication logic + +## Prerequisites + +- AEM 6.5 LTS environment with configured replication agents +- Java development environment for AEM +- Maven project with AEM dependencies +- Understanding of OSGi services and Sling ResourceResolver +- Configured replication agents (see `configure-replication-agent` skill) +- Service user or user session with replication permissions + +## Official API Documentation + +All public replication APIs are documented in the official Adobe AEM 6.5 LTS JavaDoc: + +- **Package Summary (Complete API Reference)**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) +- **Replicator Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Replicator.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Replicator.html) +- **AgentManager Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentManager.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentManager.html) +- **ReplicationQueue Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationQueue.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationQueue.html) +- **ReplicationListener Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationListener.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationListener.html) +- **AgentFilter Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentFilter.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentFilter.html) +- **Agent Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Agent.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Agent.html) +- **ReplicationAction Class**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationAction.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationAction.html) +- **ReplicationOptions Class**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationOptions.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationOptions.html) +- **ReplicationStatus Interface**: [https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationStatus.html](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationStatus.html) + +## Core API Components + +### 1. Replicator Interface + +The primary service for managing replication operations. + +**Service Type:** OSGi Service +**Package:** `com.day.cq.replication` +**Interface:** `com.day.cq.replication.Replicator` + +### 2. ReplicationActionType Enum + +Defines the type of replication operation: + +| Type | Purpose | Effect | +|------|---------|--------| +| `ACTIVATE` | Publish content | Sends to Publish instances | +| `DEACTIVATE` | Unpublish content | Removes from Publish | +| `DELETE` | Delete from Publish | Permanent removal | +| `TEST` | Test replication | Verifies connectivity | +| `REVERSE` | Reverse replicate | Publish → Author | +| `INTERNAL_POLL` | Internal polling | System use | + +### 3. ReplicationOptions Class + +Encapsulates optional parameters for replication requests. + +### 4. ReplicationStatus Interface + +Provides status information about replicated content. + +## Maven Dependencies + +The Replication API is provided by the AEM uber-jar. Add to your `pom.xml`: + +```xml + + com.adobe.aem + uber-jar + apis + provided + +``` + +The uber-jar version should match your AEM 6.5 LTS installation. The Replication API (`com.day.cq.replication.*`) is included in the uber-jar and available at runtime. + +## Replicator Interface Methods + +### Method 1: replicate(Session, ReplicationActionType, String) + +**Signature:** +```java +void replicate(Session session, + ReplicationActionType type, + String path) +throws ReplicationException +``` + +**Parameters:** +- `session` - JCR session (user permissions determine access) +- `type` - ReplicationActionType (ACTIVATE, DEACTIVATE, DELETE, etc.) +- `path` - Content path to replicate (e.g., "/content/mysite/en/page") + +**Throws:** `ReplicationException` if replication fails + +**Description:** Triggers replication for a single path with default options. + +**Example:** +```java +import com.day.cq.replication.Replicator; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Reference; + +import javax.jcr.Session; + +@Reference +private Replicator replicator; + +public void activatePage(ResourceResolver resolver, String pagePath) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath); +} +``` + +### Method 2: replicate(Session, ReplicationActionType, String, ReplicationOptions) + +**Signature:** +```java +void replicate(Session session, + ReplicationActionType type, + String path, + ReplicationOptions options) +throws ReplicationException +``` + +**Parameters:** +- `session` - JCR session +- `type` - ReplicationActionType +- `path` - Content path +- `options` - ReplicationOptions for custom configuration + +**Description:** Initiates replication with customizable options for one path. + +**Example:** +```java +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; + +public void activatePageSync(ResourceResolver resolver, String pagePath) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); // Wait for completion + opts.setSuppressVersions(true); // Don't create versions + + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath, opts); +} +``` + +### Method 3: replicate(Session, ReplicationActionType, String[], ReplicationOptions) + +**Signature:** +```java +void replicate(Session session, + ReplicationActionType type, + String[] paths, + ReplicationOptions options) +throws ReplicationException +``` + +**Parameters:** +- `session` - JCR session +- `type` - ReplicationActionType +- `paths` - Array of content paths (String[]) +- `options` - ReplicationOptions + +**Description:** Handles replication across multiple paths with supplied settings. + +**Example:** +```java +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; + +public void activateMultiplePages(ResourceResolver resolver, String[] pagePaths) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(false); // Async for better performance + + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePaths, opts); +} +``` + +### Method 4: checkPermission(Session, ReplicationActionType, String) + +**Signature:** +```java +void checkPermission(Session session, + ReplicationActionType type, + String path) +throws ReplicationException +``` + +**Parameters:** +- `session` - JCR session +- `type` - ReplicationActionType +- `path` - Content path + +**Throws:** `ReplicationException` if user lacks permissions + +**Description:** Verifies whether a user has sufficient permissions for replication activities. + +**Example:** +```java +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; + +public boolean canUserActivate(ResourceResolver resolver, String pagePath) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + try { + replicator.checkPermission(session, ReplicationActionType.ACTIVATE, pagePath); + return true; // User has permission + } catch (ReplicationException e) { + return false; // User lacks permission + } +} +``` + +### Method 5: getReplicationStatus(Session, String) + +**Signature:** +```java +ReplicationStatus getReplicationStatus(Session session, String path) +``` + +**Parameters:** +- `session` - JCR session +- `path` - Content path + +**Returns:** `ReplicationStatus` object or `null` if unavailable + +**Description:** Retrieves the replication status for a given path. + +**Example:** +```java +import com.day.cq.replication.ReplicationStatus; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; +import java.util.Calendar; + +public ReplicationInfo getPageReplicationInfo(ResourceResolver resolver, String pagePath) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + ReplicationStatus status = replicator.getReplicationStatus(session, pagePath); + + if (status != null) { + boolean isActivated = status.isActivated(); + Calendar lastPublished = status.getLastPublished(); + String lastPublishedBy = status.getLastPublishedBy(); + + return new ReplicationInfo(isActivated, lastPublished, lastPublishedBy); + } + return null; +} +``` + +### Method 6: getActivatedPaths(Session, String) + +**Signature:** +```java +Iterator getActivatedPaths(Session session, String path) +throws ReplicationException +``` + +**Parameters:** +- `session` - JCR session +- `path` - Root path for subtree + +**Returns:** Iterator of activated paths + +**Throws:** `ReplicationException` + +**Description:** Returns paths of all activated nodes for the given subtree path. + +**Example:** +```java +import com.day.cq.replication.ReplicationException; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public List getActivatedPages(ResourceResolver resolver, String rootPath) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + Iterator activatedPaths = replicator.getActivatedPaths(session, rootPath); + + List result = new ArrayList<>(); + while (activatedPaths.hasNext()) { + result.add(activatedPaths.next()); + } + return result; +} +``` + +## ReplicationOptions Class + +Encapsulates optional configuration parameters for replication requests. + +**Official Documentation:** [ReplicationOptions JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationOptions.html) + +### Key Methods: + +#### setSynchronous(boolean) + +**Description:** Controls whether replication executes synchronously (blocking) or asynchronously (default). + +**Parameters:** +- `synchronous` - `true` for synchronous, `false` for asynchronous (default) + +**Example:** +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSynchronous(true); // Wait for replication to complete +``` + +**Use cases:** +- Synchronous: When you need confirmation before proceeding (e.g., before redirecting user) +- Asynchronous: Better performance for bulk operations + +**Thread-Blocking Implications:** + +When `setSynchronous(true)` is used, the calling thread blocks until replication completes across all target agents. This has important implications: + +- **UI Performance:** In servlets or sling models rendering pages, synchronous replication blocks the HTTP request thread. For large content or slow networks, this can cause noticeable page load delays or request timeouts. +- **Thread Pool Exhaustion:** High-traffic scenarios with synchronous replication can exhaust the servlet container's request thread pool, causing cascading failures. +- **Recommended Pattern:** Use asynchronous replication (`false`, the default) for user-facing operations. Reserve synchronous mode for background jobs, workflow steps, or cases where immediate confirmation is critical for correctness (e.g., transactional workflows). + +**Performance Comparison:** +- Asynchronous: Request returns immediately after queueing (~10-50ms) +- Synchronous: Request waits for full replication cycle (500ms-5s typical, longer for large assets or network delays) + +#### setFilter(AgentFilter) + +**Description:** Sets the filter for selecting specific agents for replication. + +**Parameters:** +- `filter` - `AgentFilter` implementation + +**Example:** +```java +import com.day.cq.replication.AgentFilter; +import com.day.cq.replication.Agent; + +ReplicationOptions opts = new ReplicationOptions(); + +// Filter to specific agent +opts.setFilter(new AgentFilter() { + public boolean isIncluded(Agent agent) { + return agent.getId().equals("publish_instance_1"); + } +}); + +replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath, opts); +``` + +**Use cases:** +- Target specific publish instance +- Exclude certain agents +- Route content to specific environments + +#### setSuppressVersions(boolean) + +**Description:** Controls whether to create versions during replication. + +**Parameters:** +- `suppress` - `true` to skip version creation, `false` otherwise + +**Example:** +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSuppressVersions(true); // Don't create versions (performance) +``` + +#### setSuppressStatusUpdate(boolean) + +**Description:** Controls whether to update replication status. + +**Parameters:** +- `suppress` - `true` to skip status update, `false` otherwise + +**Example:** +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSuppressStatusUpdate(true); // Don't update status metadata +``` + +#### setRevision(String) + +**Description:** Sets the specific revision to replicate. + +**Parameters:** +- `revision` - Revision identifier + +**Example:** +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setRevision("1.0"); +replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath, opts); +``` + +## ReplicationStatus Interface + +Provides status information about replicated content. + +**Official Documentation:** [ReplicationStatus JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationStatus.html) + +### Key Methods: + +```java +// Check if content is activated +boolean isActivated(); + +// Get last published timestamp +Calendar getLastPublished(); + +// Get user who last published +String getLastPublishedBy(); + +// Get last replication action +String getLastReplicationAction(); + +// Get last replicated revision +String getLastReplicatedRevision(); +``` + +**Example:** +```java +import com.day.cq.replication.ReplicationStatus; +import org.apache.sling.api.resource.ResourceResolver; + +import javax.jcr.Session; +import java.util.HashMap; +import java.util.Map; + +public Map getReplicationMetadata(ResourceResolver resolver, String path) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + ReplicationStatus status = replicator.getReplicationStatus(session, path); + + Map metadata = new HashMap<>(); + if (status != null) { + metadata.put("activated", status.isActivated()); + metadata.put("lastPublished", status.getLastPublished()); + metadata.put("lastPublishedBy", status.getLastPublishedBy()); + metadata.put("lastAction", status.getLastReplicationAction()); + } + return metadata; +} +``` + +## Complete Implementation Examples + +### Example 1: OSGi Service with Replication + +```java +package com.mycompany.aem.core.services.impl; + +import com.day.cq.replication.Replicator; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.Session; + +@Component(service = ContentPublisher.class, immediate = true) +public class ContentPublisherImpl implements ContentPublisher { + + private static final Logger LOG = LoggerFactory.getLogger(ContentPublisherImpl.class); + + @Reference + private Replicator replicator; + + @Override + public boolean publishPage(ResourceResolver resolver, String pagePath) { + try { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + // Create options for synchronous replication + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + opts.setSuppressVersions(false); // Keep version history + + // Activate the page + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath, opts); + + LOG.info("Successfully activated page: {}", pagePath); + return true; + + } catch (ReplicationException e) { + LOG.error("Failed to activate page: {}", pagePath, e); + return false; + } + } + + @Override + public boolean unpublishPage(ResourceResolver resolver, String pagePath) { + try { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + + // Deactivate the page + replicator.replicate(session, ReplicationActionType.DEACTIVATE, pagePath, opts); + + LOG.info("Successfully deactivated page: {}", pagePath); + return true; + + } catch (ReplicationException e) { + LOG.error("Failed to deactivate page: {}", pagePath, e); + return false; + } + } +} +``` + +### Example 2: Servlet with Replication + +```java +package com.mycompany.aem.core.servlets; + +import com.day.cq.replication.Replicator; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.servlets.SlingAllMethodsServlet; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import javax.jcr.Session; +import javax.servlet.Servlet; +import java.io.IOException; + +@Component( + service = Servlet.class, + property = { + "sling.servlet.methods=POST", + "sling.servlet.paths=/bin/myapp/publish" + } +) +public class PublishServlet extends SlingAllMethodsServlet { + + @Reference + private Replicator replicator; + + @Override + protected void doPost(SlingHttpServletRequest request, + SlingHttpServletResponse response) throws IOException { + + String path = request.getParameter("path"); + String action = request.getParameter("action"); // activate or deactivate + + if (path == null || action == null) { + response.setStatus(400); + response.getWriter().write("{\"error\": \"Missing parameters\"}"); + return; + } + + // Validate path to prevent path traversal + if (!path.startsWith("/content/")) { + response.setStatus(400); + response.getWriter().write("{\"error\": \"Invalid path\"}"); + return; + } + + try { + Session session = request.getResourceResolver().adaptTo(Session.class); + if (session == null) { + response.setStatus(500); + response.getWriter().write("{\"error\": \"Unable to obtain session\"}"); + return; + } + + ReplicationActionType type = "activate".equals(action) + ? ReplicationActionType.ACTIVATE + : ReplicationActionType.DEACTIVATE; + + replicator.replicate(session, type, path); + + response.setContentType("application/json"); + response.getWriter().write("{\"success\": true, \"path\": \"" + path + "\"}"); + + } catch (ReplicationException e) { + response.setStatus(500); + response.getWriter().write("{\"error\": \"" + e.getMessage() + "\"}"); + } + } +} +``` + +### Example 3: Workflow Process Step with Replication + +```java +package com.mycompany.aem.core.workflows; + +import com.adobe.granite.workflow.WorkflowException; +import com.adobe.granite.workflow.WorkflowSession; +import com.adobe.granite.workflow.exec.WorkItem; +import com.adobe.granite.workflow.exec.WorkflowProcess; +import com.adobe.granite.workflow.metadata.MetaDataMap; +import com.day.cq.replication.Replicator; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.Session; + +@Component( + service = WorkflowProcess.class, + property = { + "process.label=Custom Replication Process" + } +) +public class CustomReplicationProcess implements WorkflowProcess { + + private static final Logger LOG = LoggerFactory.getLogger(CustomReplicationProcess.class); + + @Reference + private Replicator replicator; + + @Override + public void execute(WorkItem workItem, WorkflowSession workflowSession, + MetaDataMap metaDataMap) throws WorkflowException { + + String payloadPath = workItem.getWorkflowData().getPayload().toString(); + Session session = workflowSession.adaptTo(Session.class); + + try { + // Custom replication logic + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + + // Get config from process arguments + String processArgs = metaDataMap.get("PROCESS_ARGS", ""); + if ("activate".equals(processArgs)) { + replicator.replicate(session, ReplicationActionType.ACTIVATE, payloadPath, opts); + LOG.info("Workflow activated: {}", payloadPath); + } + + } catch (ReplicationException e) { + LOG.error("Replication failed in workflow", e); + throw new WorkflowException("Replication failed", e); + } + } +} +``` + +### Example 4: Bulk Replication with AgentFilter + +```java +import com.day.cq.replication.Agent; +import com.day.cq.replication.AgentFilter; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationException; +import com.day.cq.replication.ReplicationOptions; +import org.apache.sling.api.resource.ResourceResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.Session; +import java.util.List; + +public void bulkActivateToSpecificAgent(ResourceResolver resolver, + List paths, + String targetAgentId) + throws ReplicationException { + + // Validate parameters + if (paths == null || paths.isEmpty()) { + throw new IllegalArgumentException("Path list cannot be null or empty"); + } + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + // Configure options with agent filter + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(false); // Async for performance + opts.setFilter(new AgentFilter() { + @Override + public boolean isIncluded(Agent agent) { + return agent.getId().equals(targetAgentId); + } + }); + + // Convert List to array + String[] pathArray = paths.toArray(new String[0]); + + // Replicate all paths to specific agent + replicator.replicate(session, ReplicationActionType.ACTIVATE, pathArray, opts); + + LOG.info("Bulk activated {} paths to agent: {}", paths.size(), targetAgentId); +} +``` + +## Additional Public APIs + +Beyond the core Replicator interface, AEM 6.5 LTS provides additional public APIs for advanced replication management. These are all documented in the [official package summary](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html). + +### AgentManager Interface + +Manages replication agents programmatically. + +**Official Documentation:** [AgentManager JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentManager.html) + +**Key Method:** + +```java +Map getAgents() +``` + +Returns a map of all available agents (agent ID → Agent instance). + +**Example:** + +```java +import com.day.cq.replication.AgentManager; +import com.day.cq.replication.Agent; +import org.osgi.service.component.annotations.Reference; + +@Reference +private AgentManager agentManager; + +public void listAllAgents() { + Map agents = agentManager.getAgents(); + + for (Map.Entry entry : agents.entrySet()) { + String agentId = entry.getKey(); + Agent agent = entry.getValue(); + + LOG.info("Agent ID: {}", agentId); + LOG.info(" Enabled: {}", agent.isEnabled()); + LOG.info(" Transport URI: {}", agent.getConfiguration().getTransportURI()); + LOG.info(" Serialization Type: {}", agent.getConfiguration().getSerializationType()); + } +} + +public Agent getAgentById(String agentId) { + Map agents = agentManager.getAgents(); + return agents.get(agentId); +} + +public boolean isAgentEnabled(String agentId) { + Agent agent = agentManager.getAgents().get(agentId); + return agent != null && agent.isEnabled(); +} +``` + +### ReplicationQueue Interface + +Manages replication queue operations programmatically. + +**Official Documentation:** [ReplicationQueue JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationQueue.html) + +**Key Methods:** + +```java +// Get queue name +String getName() + +// Get all queue entries +List entries() + +// Get entries for specific path +List entries(String path) + +// Get specific entry +ReplicationQueue.Entry getEntry(String path, Calendar after) + +// Clear entire queue +void clear() + +// Clear specific entries by ID +void clear(Set ids) + +// Get queue status +ReplicationQueue.Status getStatus() + +// Check if paused +boolean isPaused() + +// Pause/resume queue +void setPaused(boolean paused) + +// Force retry on blocked entry +void forceRetry() +``` + +**Example:** + +```java +import com.day.cq.replication.Agent; +import com.day.cq.replication.ReplicationQueue; +import com.day.cq.replication.AgentManager; + +@Reference +private AgentManager agentManager; + +public void monitorQueue(String agentId) { + Agent agent = agentManager.getAgents().get(agentId); + if (agent == null) { + LOG.error("Agent not found: {}", agentId); + return; + } + + ReplicationQueue queue = agent.getQueue(); + + // Get queue status + ReplicationQueue.Status status = queue.getStatus(); + LOG.info("Queue: {}", queue.getName()); + LOG.info("Status: {}", status); + LOG.info("Is Paused: {}", queue.isPaused()); + + // List all entries + List entries = queue.entries(); + LOG.info("Queue size: {}", entries.size()); + + for (ReplicationQueue.Entry entry : entries) { + LOG.info(" Path: {}", entry.getPath()); + LOG.info(" Time: {}", entry.getTime()); + LOG.info(" ID: {}", entry.getId()); + } +} + +public void clearQueueForPath(String agentId, String path) { + Agent agent = agentManager.getAgents().get(agentId); + if (agent == null) return; + + ReplicationQueue queue = agent.getQueue(); + + // Get entries for specific path + List entries = queue.entries(path); + + if (entries.isEmpty()) { + LOG.info("No queue entries for path: {}", path); + return; + } + + // Collect entry IDs + Set ids = new HashSet<>(); + for (ReplicationQueue.Entry entry : entries) { + ids.add(entry.getId()); + } + + // Clear specific entries + queue.clear(ids); + LOG.info("Cleared {} entries for path: {}", ids.size(), path); +} + +public void pauseQueue(String agentId) { + Agent agent = agentManager.getAgents().get(agentId); + if (agent != null) { + ReplicationQueue queue = agent.getQueue(); + queue.setPaused(true); + LOG.info("Paused queue: {}", queue.getName()); + } +} + +public void forceRetryBlockedQueue(String agentId) { + Agent agent = agentManager.getAgents().get(agentId); + if (agent != null) { + ReplicationQueue queue = agent.getQueue(); + queue.forceRetry(); + LOG.info("Forced retry on queue: {}", queue.getName()); + } +} +``` + +### ReplicationListener Interface + +Listens for replication events in real-time. + +**Official Documentation:** [ReplicationListener JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationListener.html) + +**Methods:** + +```java +// Called before replication starts +void onStart(Agent agent, ReplicationAction action) + +// Called when log message written +void onMessage(ReplicationLog.Level level, String message) + +// Called when replication completes +void onEnd(Agent agent, ReplicationAction action, ReplicationResult result) + +// Called on replication error +void onError(Agent agent, ReplicationAction action, Exception exception) +``` + +**Example:** + +```java +import com.day.cq.replication.ReplicationListener; +import com.day.cq.replication.Agent; +import com.day.cq.replication.ReplicationAction; +import com.day.cq.replication.ReplicationResult; +import com.day.cq.replication.ReplicationLog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CustomReplicationListener implements ReplicationListener { + + private static final Logger LOG = LoggerFactory.getLogger(CustomReplicationListener.class); + + @Override + public void onStart(Agent agent, ReplicationAction action) { + LOG.info("Replication starting - Agent: {}, Path: {}, Type: {}", + agent.getId(), + action.getPath(), + action.getType()); + } + + @Override + public void onMessage(ReplicationLog.Level level, String message) { + switch (level) { + case DEBUG: + LOG.debug("Replication: {}", message); + break; + case INFO: + LOG.info("Replication: {}", message); + break; + case WARN: + LOG.warn("Replication: {}", message); + break; + case ERROR: + LOG.error("Replication: {}", message); + break; + } + } + + @Override + public void onEnd(Agent agent, ReplicationAction action, ReplicationResult result) { + LOG.info("Replication completed - Agent: {}, Path: {}, Success: {}", + agent.getId(), + action.getPath(), + result.isSuccess()); + + // Send notification, update metrics, etc. + if (result.isSuccess()) { + // Success handling + notifySuccess(action.getPath()); + } + } + + @Override + public void onError(Agent agent, ReplicationAction action, Exception exception) { + LOG.error("Replication failed - Agent: {}, Path: {}", + agent.getId(), + action.getPath(), + exception); + + // Error handling, alerts, retry logic + alertReplicationFailure(action.getPath(), exception); + } + + private void notifySuccess(String path) { + // Custom notification logic + } + + private void alertReplicationFailure(String path, Exception e) { + // Custom alert logic + } +} + +// Using listener with Replicator +@Reference +private Replicator replicator; + +public void replicateWithListener(ResourceResolver resolver, String path) + throws ReplicationException { + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + + // Attach custom listener + CustomReplicationListener listener = new CustomReplicationListener(); + opts.setListener(listener); + + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); +} +``` + +### AgentFilter Interface + +Filters agents for selective replication (detailed earlier). + +**Official Documentation:** [AgentFilter JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/AgentFilter.html) + +**Method:** + +```java +boolean isIncluded(Agent agent) +``` + +**Static Fields:** +- `AgentFilter.DEFAULT` - Filter for non-specific agents +- `AgentFilter.OUTBOX_AGENT_FILTER` - Filter for distribution operations + +**Advanced Example:** + +```java +import com.day.cq.replication.AgentFilter; +import com.day.cq.replication.Agent; +import com.day.cq.replication.ReplicationActionType; +import com.day.cq.replication.ReplicationOptions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +// Filter by multiple criteria +public class CustomAgentFilter implements AgentFilter { + + private final Set allowedAgentIds; + private final String requiredTransportProtocol; + + public CustomAgentFilter(Set allowedAgentIds, String protocol) { + this.allowedAgentIds = allowedAgentIds; + this.requiredTransportProtocol = protocol; + } + + @Override + public boolean isIncluded(Agent agent) { + // Filter by agent ID + if (!allowedAgentIds.contains(agent.getId())) { + return false; + } + + // Filter by enabled status + if (!agent.isEnabled()) { + return false; + } + + // Filter by transport protocol + String transportURI = agent.getConfiguration().getTransportURI(); + if (transportURI != null && !transportURI.startsWith(requiredTransportProtocol)) { + return false; + } + + return true; + } +} + +// Usage +Set allowedAgents = new HashSet<>(Arrays.asList("publish1", "publish2")); +AgentFilter filter = new CustomAgentFilter(allowedAgents, "https://"); + +ReplicationOptions opts = new ReplicationOptions(); +opts.setFilter(filter); + +replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); +``` + +### Agent Interface + +Represents a replication agent. + +**Official Documentation:** [Agent JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Agent.html) + +**Key Methods:** + +```java +String getId() // Get agent ID +boolean isEnabled() // Check if enabled +AgentConfig getConfiguration() // Get configuration +ReplicationQueue getQueue() // Get agent's queue +boolean isConnected() // Check connectivity status +``` + +**Example:** + +```java +public void inspectAgent(String agentId) { + Agent agent = agentManager.getAgents().get(agentId); + + if (agent == null) { + LOG.error("Agent not found: {}", agentId); + return; + } + + LOG.info("Agent Details:"); + LOG.info(" ID: {}", agent.getId()); + LOG.info(" Enabled: {}", agent.isEnabled()); + LOG.info(" Connected: {}", agent.isConnected()); + + AgentConfig config = agent.getConfiguration(); + LOG.info(" Transport URI: {}", config.getTransportURI()); + LOG.info(" Serialization: {}", config.getSerializationType()); + LOG.info(" User: {}", config.getTransportUser()); + + ReplicationQueue queue = agent.getQueue(); + LOG.info(" Queue Name: {}", queue.getName()); + LOG.info(" Queue Size: {}", queue.entries().size()); + LOG.info(" Queue Status: {}", queue.getStatus()); +} +``` + +## Exception Handling + +### ReplicationException Hierarchy + +```java +try { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + +} catch (ReplicationException e) { + // Base exception for all replication errors + LOG.error("Replication failed", e); + + // Common causes: + // - Agent not found + // - Agent disabled or blocked + // - Network connectivity issues + // - Permission denied + // - Target instance unavailable +} +``` + +### Best Practices for Exception Handling: + +```java +public ReplicationResult safeReplicate(ResourceResolver resolver, String path) { + try { + // Check permissions first + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.checkPermission(session, ReplicationActionType.ACTIVATE, path); + + // Perform replication + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(true); + replicator.replicate(session, ReplicationActionType.ACTIVATE, path, opts); + + return ReplicationResult.success(path); + + } catch (ReplicationException e) { + LOG.error("Replication failed for path: {}", path, e); + return ReplicationResult.failure(path, e.getMessage()); + } +} +``` + +## Error Handling Patterns + +Choose the appropriate error handling pattern based on your use case. + +### Pattern 1: Throw Exceptions (Library Code) + +**When to use:** +- Library/utility methods +- Workflow process steps +- OSGi services called by other components +- When caller needs to handle errors differently + +**Characteristics:** +- Propagates errors to caller +- Caller decides error handling strategy +- Most flexible for reuse + +**Example:** +```java +public void activatePage(ResourceResolver resolver, String pagePath) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath); + // Throws ReplicationException on failure +} +``` + +**Caller handles the exception:** +```java +try { + activatePage(resolver, path); + LOG.info("Page activated successfully: {}", path); +} catch (ReplicationException e) { + LOG.error("Activation failed: {}", path, e); + // Caller-specific error handling (retry, alert, etc.) +} +``` + +### Pattern 2: Return Boolean (Service Layer) + +**When to use:** +- Service layer with internal error logging +- Fire-and-forget operations +- When caller only needs success/failure indicator +- Background/scheduled jobs + +**Characteristics:** +- Logs errors internally +- Returns simple success/failure flag +- Caller doesn't need exception details + +**Example:** +```java +public boolean activatePage(ResourceResolver resolver, String pagePath) { + try { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + LOG.error("Unable to adapt ResourceResolver to Session"); + return false; + } + + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath); + LOG.info("Page activated: {}", pagePath); + return true; + + } catch (ReplicationException e) { + LOG.error("Replication failed for: {}", pagePath, e); + return false; + } +} +``` + +**Caller usage:** +```java +if (activatePage(resolver, path)) { + // Success path + updateAuditLog(path, "activated"); +} else { + // Failure path + updateAuditLog(path, "activation_failed"); +} +``` + +### Pattern 3: HTTP Status Codes (Servlets and REST APIs) + +**When to use:** +- Sling servlets +- REST API endpoints +- Web service integrations +- HTTP-based interfaces + +**Characteristics:** +- Communicates errors via HTTP status codes +- Returns error details in response body +- Standard web semantics + +**Example:** +```java +@Component( + service = Servlet.class, + property = { + "sling.servlet.methods=POST", + "sling.servlet.paths=/bin/myapp/replicate" + } +) +public class ReplicationServlet extends SlingAllMethodsServlet { + + @Reference + private Replicator replicator; + + @Override + protected void doPost(SlingHttpServletRequest request, + SlingHttpServletResponse response) throws IOException { + + String path = request.getParameter("path"); + + // Validate parameters + if (path == null || path.trim().isEmpty()) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.getWriter().write("{\"error\": \"Missing required parameter: path\"}"); + return; + } + + // Validate path + if (!path.startsWith("/content/")) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.getWriter().write("{\"error\": \"Invalid path: must start with /content/\"}"); + return; + } + + try { + Session session = request.getResourceResolver().adaptTo(Session.class); + if (session == null) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.getWriter().write("{\"error\": \"Unable to obtain session\"}"); + return; + } + + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().write("{\"success\": true, \"path\": \"" + path + "\"}"); + + } catch (ReplicationException e) { + LOG.error("Replication failed", e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.getWriter().write("{\"error\": \"" + e.getMessage() + "\"}"); + } + } +} +``` + +### Pattern Comparison + +| Pattern | Use Case | Error Details | Caller Control | Example Context | +|---------|----------|---------------|----------------|-----------------| +| **Throw Exception** | Library code | Full stack trace | High - caller handles | Utility methods, workflow steps | +| **Return Boolean** | Service layer | Logged internally | Low - success/fail only | Background jobs, scheduled tasks | +| **HTTP Status** | Web interfaces | JSON error response | Medium - status code + message | REST APIs, servlets | + +### Choosing the Right Pattern + +**Use Throw Exception when:** +- Building reusable library code +- Caller needs detailed error information +- Different callers need different error handling +- Workflow process steps (WorkflowProcess interface) + +**Use Return Boolean when:** +- Errors are logged and monitored centrally +- Caller only needs success/failure indicator +- Fire-and-forget operations +- Scheduled/background jobs + +**Use HTTP Status when:** +- Building HTTP endpoints (servlets, REST) +- Web service integrations +- Following REST API conventions +- Client needs machine-readable error codes + +### Anti-Patterns to Avoid + +**Don't mix patterns:** +```java +// BAD: Returning boolean but also throwing exception +public boolean activatePage(String path) throws ReplicationException { + // Confusing - which error handling mechanism? +} +``` + +**Don't swallow exceptions silently:** +```java +// BAD: Silent failure +try { + replicator.replicate(session, type, path); +} catch (ReplicationException e) { + // No logging, no error indication +} +return true; // Always returns true, even on failure +``` + +**Don't use generic exceptions:** +```java +// BAD: Generic exception loses context +public void activate(String path) throws Exception { + // Caller can't distinguish ReplicationException from other errors +} +``` + +**Do be specific:** +```java +// GOOD: Specific exception type +public void activate(String path) throws ReplicationException { + // Caller knows exactly what failed +} +``` + +## Security Considerations + +### 1. Use Service Users + +Never use admin sessions. Create dedicated service users: + +**Service User Mapping OSGi Configuration:** + +Create file: `ui.config/src/main/content/jcr_root/apps/myapp/osgiconfig/config.author/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-replication.cfg.json` + +```json +{ + "user.mapping": [ + "com.mycompany.aem.core:replication-service=replication-service" + ] +} +``` + +**Service User Permissions:** + +The service user `replication-service` requires: +- Read permissions on `/content`, `/conf` +- Replicate permissions on paths to be activated +- Write permissions on `/var/replication/outbox` (for reverse replication) + +```java +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.api.resource.ResourceResolver; + +@Reference +private ResourceResolverFactory resolverFactory; + +public void replicateWithServiceUser(String path) throws ReplicationException { + Map authInfo = new HashMap<>(); + authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); + + try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo)) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + } catch (LoginException e) { + LOG.error("Service user login failed", e); + } +} +``` + +### 2. Permission Checks + +Always verify permissions before replication: + +```java +public boolean activateIfAllowed(ResourceResolver resolver, String path) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + try { + // Check permission first + replicator.checkPermission(session, ReplicationActionType.ACTIVATE, path); + + // Permission granted, proceed + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + return true; + + } catch (ReplicationException e) { + LOG.warn("User lacks replication permission for: {}", path); + return false; + } +} +``` + +### 3. Input Validation + +Validate paths and parameters: + +```java +public void safeActivate(ResourceResolver resolver, String path) + throws IllegalArgumentException, ReplicationException { + + // Validate path + if (path == null || path.trim().isEmpty()) { + throw new IllegalArgumentException("Path cannot be null or empty"); + } + + // Ensure path is under /content + if (!path.startsWith("/content/")) { + throw new IllegalArgumentException("Invalid path: must be under /content"); + } + + // Check resource exists + Resource resource = resolver.getResource(path); + if (resource == null) { + throw new IllegalArgumentException("Resource not found: " + path); + } + + // Validate resource type is replicable + if (!isReplicableResource(resource)) { + throw new IllegalArgumentException( + "Resource is not replicable. Must be cq:Page or dam:Asset, found: " + + resource.getResourceType()); + } + + // Safe to replicate + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); +} + +/** + * Validates if a resource is a replicable content type. + * + *

This method checks if the resource represents content that should be replicated + * to publish instances. Only standard AEM content types (pages, assets, experience fragments) + * are considered replicable. System and configuration resources are excluded to prevent + * unintended replication of application code or configuration.

+ * + *

Allowed resource types:

+ *
    + *
  • cq:Page - Standard AEM pages and their hierarchies
  • + *
  • dam:Asset - Digital Asset Manager (DAM) assets including images, videos, documents
  • + *
  • cq:PageContent under /content/experience-fragments/ - Experience fragments for content reuse
  • + *
+ * + *

Excluded paths: Resources under /apps, /libs, + * /etc/designs, /var, and other system paths are implicitly excluded + * by only accepting the types above.

+ * + * @param resource the resource to validate (must not be null) + * @return {@code true} if the resource is a standard content type that should be replicated; + * {@code false} if it is a system/configuration resource or unsupported type + * @see AEM Replication Documentation + */ +private boolean isReplicableResource(Resource resource) { + String resourceType = resource.getResourceType(); + + // Check if it's a page + if (resource.isResourceType("cq:Page")) { + return true; + } + + // Check if it's a DAM asset + if (resource.isResourceType("dam:Asset")) { + return true; + } + + // Check if it's an experience fragment + if (resource.isResourceType("cq:PageContent") && + resource.getPath().startsWith("/content/experience-fragments/")) { + return true; + } + + // System/config resources should not be replicated + return false; +} +``` + +## ResourceResolver Lifecycle Management + +### Caller Responsibility Pattern + +**Important:** When accepting `ResourceResolver` as a method parameter, the **caller** is responsible for closing it, not the method. + +```java +/** + * Activates a page using the replication API. + * + * @param resolver ResourceResolver (caller must close) + * @param pagePath Path to the page to activate + * @throws ReplicationException if replication fails + */ +public void activatePage(ResourceResolver resolver, String pagePath) + throws ReplicationException { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath); + // Do NOT close resolver here - caller owns it +} +``` + +**Caller usage:** +```java +ResourceResolver resolver = null; +try { + resolver = resolverFactory.getServiceResourceResolver(authInfo); + activatePage(resolver, "/content/mysite/en/page"); +} catch (LoginException e) { + LOG.error("Service user login failed", e); +} catch (ReplicationException e) { + LOG.error("Replication failed", e); +} finally { + if (resolver != null && resolver.isLive()) { + resolver.close(); // Caller closes + } +} +``` + +### Try-with-Resources Pattern (Recommended) + +When creating the `ResourceResolver` within your method, use try-with-resources for automatic cleanup: + +```java +public void activateWithServiceUser(String pagePath) throws ReplicationException { + Map authInfo = new HashMap<>(); + authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); + + try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo)) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, pagePath); + } catch (LoginException e) { + LOG.error("Service user login failed", e); + throw new ReplicationException("Authentication failed", e); + } + // ResourceResolver auto-closed here +} +``` + +### Resource Leak Prevention + +**Common mistake - Resource leak:** +```java +// BAD: Resolver never closed +public void activatePage(String path) throws ReplicationException { + ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo); + Session session = resolver.adaptTo(Session.class); + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + // LEAK: resolver not closed +} +``` + +**Correct pattern:** +```java +// GOOD: Try-with-resources ensures cleanup +public void activatePage(String path) throws ReplicationException { + Map authInfo = new HashMap<>(); + authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); + + try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authInfo)) { + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + } catch (LoginException e) { + LOG.error("Service user login failed", e); + throw new ReplicationException("Authentication failed", e); + } +} +``` + +### Manual Close Pattern (Legacy) + +If try-with-resources is not an option (pre-Java 7 code): + +```java +public void activatePage(String path) throws ReplicationException { + ResourceResolver resolver = null; + try { + Map authInfo = new HashMap<>(); + authInfo.put(ResourceResolverFactory.SUBSERVICE, "replication-service"); + resolver = resolverFactory.getServiceResourceResolver(authInfo); + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); + + } catch (LoginException e) { + LOG.error("Service user login failed", e); + throw new ReplicationException("Authentication failed", e); + } finally { + if (resolver != null && resolver.isLive()) { + resolver.close(); + } + } +} +``` + +### Best Practices + +1. **Prefer try-with-resources** for all new code +2. **Document ownership** with JavaDoc when accepting ResourceResolver as parameter +3. **Always check isLive()** before closing in finally blocks +4. **Never close** a ResourceResolver you didn't create +5. **Use service users** - never create admin ResourceResolvers + +## Performance Optimization + +### 1. Use Asynchronous Replication for Bulk Operations + +```java +public void bulkActivate(ResourceResolver resolver, List paths) + throws ReplicationException { + + Session session = resolver.adaptTo(Session.class); + if (session == null) { + throw new IllegalStateException("Unable to adapt ResourceResolver to Session"); + } + + ReplicationOptions opts = new ReplicationOptions(); + opts.setSynchronous(false); // Non-blocking for better performance + opts.setSuppressVersions(true); // Skip versioning to reduce overhead + + String[] pathArray = paths.toArray(new String[0]); + replicator.replicate(session, ReplicationActionType.ACTIVATE, pathArray, opts); +} +``` + +### 2. Batch Multiple Paths + +Use array variants instead of looping: + +```java +// BAD: Individual calls in loop +for (String path : paths) { + replicator.replicate(session, ReplicationActionType.ACTIVATE, path); +} + +// GOOD: Single batch call +String[] pathArray = paths.toArray(new String[0]); +replicator.replicate(session, ReplicationActionType.ACTIVATE, pathArray, opts); +``` + +### 3. Suppress Unnecessary Operations + +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSuppressVersions(true); // Don't create versions +opts.setSuppressStatusUpdate(false); // Keep status tracking +``` + +## Testing and Validation + +### Unit Testing with Mocks + +```java +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ContentPublisherTest { + + @Mock + private Replicator replicator; + + @Mock + private ResourceResolver resolver; + + @Mock + private Session session; + + @InjectMocks + private ContentPublisherImpl contentPublisher; + + @Test + void testPublishPage() throws Exception { + when(resolver.adaptTo(Session.class)).thenReturn(session); + + String path = "/content/mysite/en/page"; + boolean result = contentPublisher.publishPage(resolver, path); + + assertTrue(result); + verify(replicator).replicate( + eq(session), + eq(ReplicationActionType.ACTIVATE), + eq(path), + any(ReplicationOptions.class) + ); + } +} +``` + +## Troubleshooting + +### Common Issues: + +**Issue: ReplicationException - Agent not found** +``` +Solution: Verify replication agents are configured and enabled +Check: /etc/replication/agents.author +``` + +**Issue: Permission denied** +``` +Solution: Grant replication privileges to service user +Check: User permissions on content path and crx:replicate privilege +``` + +**Issue: Synchronous replication times out** +``` +Solution: +1. Use asynchronous replication for large operations +2. Increase timeout in replication agent configuration +3. Check Publish instance performance +``` + +**Issue: Content not appearing on Publish** +``` +Solution: +1. Check replication queue status +2. Verify Dispatcher Flush agent is working +3. Check Publish instance logs +4. Use getReplicationStatus() to verify activation +``` + +## Related Skills + +- `configure-replication-agent`: Set up replication agents +- `replicate-content`: UI-based content activation methods +- `troubleshoot-replication`: Diagnose and fix replication issues + +## Success Criteria + +- ✓ Successfully integrated Replicator service via OSGi reference +- ✓ Programmatic activation/deactivation working +- ✓ ReplicationOptions configured correctly +- ✓ Permission checks implemented +- ✓ Exception handling in place +- ✓ Service user configured with proper permissions +- ✓ Unit tests passing with mocked Replicator +- ✓ Replication status can be queried +- ✓ Content appears on Publish after API call +- ✓ Logs show successful replication entries + +## Additional Resources + +- [Replication API JavaDoc (Complete Package Summary)](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) +- [Replicator Interface JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/Replicator.html) +- [ReplicationOptions JavaDoc](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/ReplicationOptions.html) +- [Official AEM 6.5 LTS Replication Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication) +- [AEM 6.5 LTS Replication Troubleshooting Guide](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep) +- [AEM 6.5 LTS Documentation Hub](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts) +- AEM Developer Documentation for OSGi services +- ACS AEM Commons Replication Examples: [GitHub](https://github.com/Adobe-Consulting-Services/acs-aem-commons) diff --git a/skills/aem/6.5-lts/skills/aem-replication/replication-orchestrator/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/replication-orchestrator/SKILL.md new file mode 100644 index 0000000..8f72f2a --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/replication-orchestrator/SKILL.md @@ -0,0 +1,472 @@ +--- +name: replication-orchestrator +description: | + Orchestrates end-to-end replication workflows spanning multiple concerns: new environment setup, + production incident response, and performance optimization for AEM 6.5 LTS. +--- + +# Replication Orchestrator + +Coordinates complex replication workflows that span multiple sub-skills (configure, replicate, troubleshoot). + +## When to Use This Skill + +Use the orchestrator for multi-step scenarios requiring coordination across sub-skills: +- **New Environment Setup:** Configure agents → Test replication → Troubleshoot issues +- **Production Incidents:** Diagnose problem → Isolate root cause → Fix and verify +- **Performance Optimization:** Monitor metrics → Tune configuration → Validate improvements +- **Migration Preparation:** Audit current setup → Document dependencies → Plan cutover + +For single-concern tasks, use the specific sub-skill directly instead of the orchestrator. + +## Workflow 1: New Environment Setup + +End-to-end workflow for setting up replication in a new AEM 6.5 LTS environment. + +### Prerequisites + +- Author instance running and accessible +- Publish instance(s) running and accessible +- Dispatcher installed and configured +- Service user accounts created +- Network connectivity verified + +### Steps + +#### 1. Configure Default Replication Agent + +**Delegate to:** [configure-replication-agent](../configure-replication-agent/SKILL.md) + +**Actions:** +1. Create default replication agent on Author +2. Configure transport URI: `http://publish-host:4503/bin/receive?sling:authRequestLogin=1` +3. Set service user credentials +4. Enable the agent + +**Verification Checkpoint:** +```bash +# Test agent connectivity +curl -u $AEM_USER:$AEM_PASSWORD \ + http://localhost:4502/etc/replication/agents.author/.test.html +``` + +Expected: "Replication test succeeded" + +#### 2. Configure Dispatcher Flush Agent + +**Delegate to:** [configure-replication-agent](../configure-replication-agent/SKILL.md) + +**Actions:** +1. Create flush agent on each Publish instance +2. Configure transport URI: `http://dispatcher-host:80/dispatcher/invalidate.cache` +3. Set serialization type to "Dispatcher Flush" +4. Enable the agent + +**Verification Checkpoint:** +```bash +# Test flush agent connectivity +curl -u $AEM_USER:$AEM_PASSWORD \ + http://publish-host:4503/etc/replication/agents.publish/flush.test.html +``` + +Expected: "Replication (Dispatcher Flush) test succeeded" + +#### 3. Test Content Replication + +**Delegate to:** [replicate-content](../replicate-content/SKILL.md) + +**Actions:** +1. Create test page: `/content/test/replication-check` +2. Activate via Quick Publish +3. Verify on Publish instance +4. Verify Dispatcher cache invalidation + +**Verification Checkpoint:** +```bash +# Check page on Publish +curl http://publish-host:4503/content/test/replication-check.html + +# Check page on Dispatcher +curl http://dispatcher-host:80/content/test/replication-check.html + +# Verify cache was invalidated (should see fresh content) +``` + +Expected: Page content identical on all instances + +#### 4. Configure Monitoring + +**Actions:** +1. Enable JMX monitoring for queue metrics +2. Set up log monitoring for replication errors +3. Configure alerts for queue depth > 20 items +4. Document runbook for common issues + +**JMX Bean:** +``` +com.day.cq.replication:type=Agent,id= + - QueueNumEntries + - QueueBlocked + - QueueProcessingSince +``` + +#### 5. Handle Any Issues + +**If problems occur, delegate to:** [troubleshoot-replication](../troubleshoot-replication/SKILL.md) + +**Common setup issues:** +- Connection refused → Verify target instance running and network connectivity +- 401 Unauthorized → Check service user credentials +- Queue blocked → Review error.log for root cause +- Content not appearing → Check Dispatcher cache invalidation + +### Success Criteria + +- [ ] Default replication agent enabled and passing test +- [ ] Dispatcher flush agent enabled and passing test +- [ ] Test page successfully replicated to Publish +- [ ] Test page accessible via Dispatcher with correct cache headers +- [ ] JMX monitoring configured and showing metrics +- [ ] Log monitoring configured for replication errors +- [ ] Team runbook updated with agent details + +## Workflow 2: Production Incident Response + +End-to-end workflow for diagnosing and resolving production replication issues. + +### Incident Triage + +#### 1. Gather Symptoms + +**Questions to answer:** +- Is content replicating at all? (None vs. Some) +- Which agents are affected? (All vs. Specific) +- When did the issue start? (Timestamp) +- What changed recently? (Deployments, config, network) + +**Data to collect:** +```bash +# Check agent status +curl -u $AEM_USER:$AEM_PASSWORD \ + http://localhost:4502/etc/replication/agents.author/.html + +# Check queue depth +# Navigate to JMX Console: /system/console/jmx +# com.day.cq.replication:type=Agent,id= +# QueueNumEntries value + +# Check recent errors +tail -n 100 /crx-quickstart/logs/error.log | grep -i replication +``` + +#### 2. Diagnose Root Cause + +**Delegate to:** [troubleshoot-replication](../troubleshoot-replication/SKILL.md) + +**Follow diagnostic decision tree:** +1. Is queue blocked? → Network/connectivity issue +2. Are there 401/403 errors? → Authentication issue +3. Are there SSL errors? → Certificate issue +4. Is queue depth growing? → Target instance overloaded +5. Is content missing on Dispatcher? → Cache invalidation issue + +**Common root causes:** +- Network partition between Author and Publish +- Service user credentials expired or revoked +- Target instance CPU/memory exhausted +- Dispatcher not accepting flush requests +- Firewall rule change blocking replication traffic + +#### 3. Implement Fix + +**Based on diagnosis:** + +**Network Issue:** +- Verify network connectivity: `ping publish-host` +- Check firewall rules +- Test replication port: `telnet publish-host 4503` + +**Authentication Issue:** +- Verify service user exists and is active +- Check user permissions: `/useradmin` +- Regenerate credentials if expired + +**Target Capacity Issue:** +- Monitor Publish instance CPU/memory +- Scale horizontally (add publish instances) +- Optimize Publish instance configuration + +**Dispatcher Issue:** +- Verify Dispatcher flush agent configuration +- Check Dispatcher allowedClients setting +- Restart Dispatcher if necessary + +#### 4. Verify Resolution + +**Validation steps:** +1. Clear blocked queue items (if applicable) +2. Retry failed replications +3. Activate test content +4. Monitor queue depth for 15 minutes +5. Verify no new errors in logs + +**Verification commands:** +```bash +# Retry queue via JMX +# com.day.cq.replication:type=Agent,id= +# Operation: retryFirst() + +# Monitor queue depth +watch -n 5 'curl -s -u $AEM_USER:$AEM_PASSWORD \ + http://localhost:4502/system/console/jmx/com.day.cq.replication%3Atype%3DAgent%2Cid%3D \ + | grep QueueNumEntries' +``` + +#### 5. Post-Incident Review + +**Document:** +- Root cause analysis +- Timeline of incident +- Resolution steps taken +- Preventive measures for future + +**Update runbooks with:** +- New diagnostic patterns observed +- Effective resolution procedures +- Monitoring improvements needed + +### Success Criteria + +- [ ] Root cause identified and documented +- [ ] Fix implemented and verified +- [ ] Queue processing normally (depth decreasing) +- [ ] No errors in replication.log for 15 minutes +- [ ] Test content replicates successfully +- [ ] Monitoring confirms normal operation +- [ ] Post-incident review completed +- [ ] Runbook updated + +## Workflow 3: Performance Optimization + +End-to-end workflow for improving replication throughput and efficiency. + +### Performance Baseline + +#### 1. Measure Current Performance + +**Metrics to collect:** +- Average replication rate (pages/minute) +- Queue depth over time +- Replication latency (activation to publish) +- Target instance CPU/memory utilization +- Network latency (Author to Publish) + +**Measurement period:** 7 days of production traffic + +**Tools:** +- JMX metrics for queue depth +- replication.log for timing analysis +- System monitoring for resource utilization + +#### 2. Identify Bottlenecks + +**Common bottlenecks:** + +**High Queue Depth:** +- Cause: Target instance slow to process +- Indicator: Queue depth consistently > 20 items +- Delegate to: Capacity planning + +**Slow Network:** +- Cause: High latency or low bandwidth +- Indicator: Large assets take > 5 minutes +- Delegate to: Network team for optimization + +**Serialization Overhead:** +- Cause: Large page structures or many components +- Indicator: CPU spikes during activation +- Delegate to: Content architecture review + +**Synchronous Replication:** +- Cause: Using synchronous options unnecessarily +- Indicator: UI blocks during activation +- Solution: Switch to asynchronous + +#### 3. Implement Optimizations + +**Based on bottleneck analysis:** + +**Optimization 1: Use Asynchronous Replication** + +**Delegate to:** [replication-api](../replication-api/SKILL.md) + +```java +ReplicationOptions opts = new ReplicationOptions(); +opts.setSynchronous(false); // Don't block +replicator.replicate(session, type, paths, opts); +``` + +**When to use:** Bulk operations, background jobs, non-critical content + +**Optimization 2: Batch Replication** + +**Delegate to:** [replication-api](../replication-api/SKILL.md) + +```java +String[] paths = contentPaths.toArray(new String[0]); +replicator.replicate(session, type, paths, opts); +``` + +**Batch size:** 100-500 paths (balance throughput vs. memory) + +**Optimization 3: Increase Target Capacity** + +**Actions:** +- Add additional Publish instances +- Create agents for each instance +- Load balance across instances + +**Expected improvement:** Linear scaling with instance count + +**Optimization 4: Tune Timeouts** + +**For large DAM assets:** +``` +Connection Timeout: 30000ms +Socket Timeout: 60000ms +``` + +**For standard pages:** +``` +Connection Timeout: 10000ms (default) +Socket Timeout: 10000ms (default) +``` + +#### 4. Measure Improvements + +**Compare metrics:** +- Replication rate before/after +- Queue depth before/after +- User-reported activation time + +**Expected improvements:** +- 2-5x throughput increase with async +- 50% reduction in queue depth with batching +- Linear scaling with additional instances + +**If improvements insufficient:** +- Review content architecture for optimization opportunities +- Consider workflow-based approval to reduce replication volume +- Evaluate network infrastructure upgrades + +### Success Criteria + +- [ ] Baseline performance metrics documented +- [ ] Bottlenecks identified with data +- [ ] Optimizations implemented and tested +- [ ] Performance improvements measured and validated +- [ ] Monitoring updated to track new metrics +- [ ] Documentation updated with optimization patterns + +## Workflow 4: Migration Preparation (AEM 6.5 LTS → Cloud Service) + +Prepare for migration to AEM as a Cloud Service Sling Distribution API. + +### Migration Assessment + +#### 1. Audit Current Replication Setup + +**Inventory:** +- Number of replication agents +- Custom replication code locations +- Agent configuration dependencies +- Integration points with external systems + +**Questions:** +- Which code uses Replication API directly? +- Are there agent ID filters in code? (tight coupling) +- Are there custom replication listeners? +- Are there scheduled/automated replication jobs? + +#### 2. Identify Migration Effort + +**High-effort areas:** +- Custom `ReplicationListener` implementations → Event listener refactor +- Agent ID filtering → Configuration-driven approach needed +- Direct agent configuration manipulation → Remove or abstract +- Custom serialization types → Standard Cloud Service patterns + +**Low-effort areas:** +- Simple `Replicator.replicate()` calls → Map to Distribution API +- Standard workflows → Cloud Service equivalents exist +- Service user patterns → Continue in Cloud Service + +#### 3. Plan Migration Strategy + +**Phases:** + +**Phase 1: Code Audit** (delegate to development team) +- Grep for `com.day.cq.replication` package imports +- Document all usage locations +- Categorize by migration complexity + +**Phase 2: Abstraction Layer** (reduce coupling) +- Create interface wrapping replication operations +- Implement with current Replication API +- Update code to use interface instead of direct API + +**Phase 3: Cloud Service Implementation** +- Implement interface with Sling Distribution API +- Deploy to Cloud Service dev environment +- Test functional equivalence + +**Phase 4: Cutover** +- Deploy to Cloud Service staging +- Run parallel for validation period +- Cut over production traffic + +#### 4. Reference Migration Patterns + +**Official Adobe Cloud Service Documentation:** + +- [Content Distribution in AEM as a Cloud Service](https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/operations/distribution.html) +- [Migrating to AEM as a Cloud Service](https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/migration-journey/getting-started.html) + +**Key API migration patterns:** +- `Replicator.replicate()` → Sling Distribution API (no direct replacement, use content distribution patterns) +- `ReplicationOptions` → Configuration via OSGi for distribution agents +- `ReplicationActionType.ACTIVATE` → Content publish workflows in Cloud Service +- Agent configuration → Cloud-native distribution managed by Adobe + +**Important:** AEM as a Cloud Service uses a fundamentally different architecture. The Replication API does not exist in Cloud Service. Instead, content distribution is handled automatically by the platform. Custom replication code must be refactored to use Cloud Service's content publishing workflows. + +### Success Criteria + +- [ ] Complete inventory of replication usage +- [ ] Migration effort estimated +- [ ] Abstraction layer designed +- [ ] Proof-of-concept validated on Cloud Service +- [ ] Migration plan documented and approved +- [ ] Team trained on Cloud Service patterns + +## Cross-Skill Coordination + +The orchestrator coordinates across specialist skills: + +- **Configure:** [configure-replication-agent/SKILL.md](../configure-replication-agent/SKILL.md) +- **Replicate:** [replicate-content/SKILL.md](../replicate-content/SKILL.md) +- **API:** [replication-api/SKILL.md](../replication-api/SKILL.md) +- **Troubleshoot:** [troubleshoot-replication/SKILL.md](../troubleshoot-replication/SKILL.md) + +## Related Skills + +- **AEM Workflow:** Integrate replication with approval workflows +- **Dispatcher:** Configure cache invalidation and flush agents +- **AEM Cloud Service Best Practices:** Migration patterns for Cloud Service + +## Foundation References + +- [Agent Types](../references/replication-foundation/agent-types.md) +- [Queue Mechanics](../references/replication-foundation/queue-mechanics.md) +- [AEM 6.5 LTS Guardrails](../references/replication-foundation/65-lts-guardrails.md) +- [API Quick Reference](../references/replication-foundation/api-reference.md) diff --git a/skills/aem/6.5-lts/skills/aem-replication/troubleshoot-replication/SKILL.md b/skills/aem/6.5-lts/skills/aem-replication/troubleshoot-replication/SKILL.md new file mode 100644 index 0000000..af5063c --- /dev/null +++ b/skills/aem/6.5-lts/skills/aem-replication/troubleshoot-replication/SKILL.md @@ -0,0 +1,1065 @@ +--- +name: troubleshoot-replication +description: Diagnose and fix common AEM 6.5 LTS replication issues including blocked queues, connectivity failures, and content distribution problems +--- + +# Troubleshoot AEM 6.5 LTS Replication + +This skill provides systematic troubleshooting guidance for Adobe Experience Manager 6.5 LTS replication issues. Use this to diagnose and resolve problems with content distribution, agent configuration, and replication workflows. + +## When to Use This Skill + +Use this skill when experiencing: +- Replication queues blocked or stuck +- Content not appearing on Publish instances +- Replication agent showing red/error status +- "Replication triggered, but no agent found" errors +- Timeout errors during replication +- Authentication failures (401 Unauthorized) +- Connection refused errors +- Slow or delayed replication +- Dispatcher cache not invalidating +- Reverse replication failures +- Missing or incorrect replication status + +## Prerequisites + +- AEM 6.5 LTS Author and Publish instances +- Administrator access to AEM environments +- Access to replication agent configuration +- Access to log files (error.log, replication.log) +- Understanding of your replication topology + +## Diagnostic Workflow + +Follow this systematic approach to identify and resolve replication issues: + +``` +1. Verify Symptoms + ↓ +2. Check Agent Status + ↓ +3. Review Replication Queue + ↓ +4. Test Connectivity + ↓ +5. Examine Logs + ↓ +6. Verify Configuration + ↓ +7. Apply Fix + ↓ +8. Validate Resolution +``` + +## Common Issues and Solutions + +### Issue 1: Replication Queue Blocked + +**Symptoms:** +- Red status indicator on replication agent +- Queue shows items waiting +- First item in queue failed +- Subsequent items cannot process (FIFO blocking) + +**Diagnosis:** + +1. **Check agent status:** + ``` + Navigate to: Tools → Deployment → Replication → Agents on author + Look for: Red indicator next to agent name + ``` + +2. **View queue details:** + ``` + Click agent name + Review queue entries + Check error message on failed item + ``` + +**Root Causes:** +- Network connectivity lost to Publish instance +- Publish instance down or unavailable +- Authentication credentials expired or incorrect +- Insufficient permissions on target content +- Disk space full on Publish +- Large package timeout +- SSL/TLS certificate issues + +**Solutions:** + +**Solution A: Retry Failed Item** +``` +Steps: +1. Open blocked replication agent +2. Click "Force Retry" button +3. Monitor queue to see if item processes +4. If successful, remaining items will process automatically +``` + +**Solution B: Clear Failed Item** +``` +Steps: +1. Open blocked replication agent +2. Select failed item in queue +3. Click "Clear" to remove it +4. Remaining items will process +5. Manually re-replicate cleared content if needed +``` + +**Solution C: Restart Replication Components** +``` +Navigate to: /system/console/bundles +Search for: "replication" + +Restart these bundles: +- com.day.cq.cq-replication +- com.day.cq.cq-replication-audit +- com.day.cq.wcm.cq-wcm-replication + +Steps: +1. Find bundle +2. Click "Stop" +3. Wait for status: Resolved +4. Click "Start" +5. Verify status: Active +``` + +**Solution D: Restart Event Processing** +``` +OSGi Console: /system/console/bundles +Restart: Apache Sling Event Support (org.apache.sling.event) + +This clears event queue backlogs +``` + +### Issue 2: Connection Refused + +**Symptoms:** +- Error: "Connection refused" +- Test connection fails +- Replication queue blocked with connectivity errors + +**Diagnosis:** + +1. **Verify Publish instance is running:** + ```bash + # Check if Publish is accessible + curl -I http://publish-host:4503/system/console + + # Or browse to: + http://publish-host:4503/system/console + ``` + +2. **Test network connectivity:** + ```bash + # From Author server + telnet publish-host 4503 + + # Or + nc -zv publish-host 4503 + + # Or + ping publish-host + ``` + +3. **Check replication agent URI:** + ``` + Navigate to: Agent → Edit → Transport tab + Verify: URI matches Publish host and port + Expected: http://publish-host:4503/bin/receive?sling:authRequestLogin=1 + ``` + +**Root Causes:** +- Publish instance not running +- Firewall blocking connection +- Incorrect hostname or port in agent configuration +- Network routing issues +- DNS resolution failures + +**Solutions:** + +**Solution A: Start Publish Instance** +```bash +cd /path/to/publish/crx-quickstart +./bin/start +``` + +**Solution B: Fix Network/Firewall** +``` +1. Verify firewall rules allow Author → Publish on port 4503 +2. Check network ACLs and security groups (cloud environments) +3. Verify no proxy blocking connection +4. Test from Author server command line +``` + +**Solution C: Correct Agent URI** +``` +Steps: +1. Edit replication agent +2. Transport tab +3. Update URI to correct host/port: + http://correct-publish-host:4503/bin/receive?sling:authRequestLogin=1 +4. Save +5. Test Connection +``` + +### Issue 3: 401 Unauthorized + +**Symptoms:** +- Error: "401 Unauthorized" +- Authentication failures in logs +- Test connection fails with credential error + +**Diagnosis:** + +1. **Check agent credentials:** + ``` + Agent → Edit → Transport tab + Verify: User and Password fields + ``` + +2. **Verify user exists on Publish:** + ``` + Publish instance: http://publish:4503/crx/explorer + Navigate to: /home/users + Search for: replication service user + ``` + +3. **Check user permissions:** + ``` + On Publish instance: + User → Permissions + Required: Read, Write, Replicate privileges + ``` + +**Root Causes:** +- Incorrect username or password +- User doesn't exist on target instance +- User password changed +- User disabled or locked +- Insufficient permissions + +**Solutions:** + +**Solution A: Update Credentials** +``` +Steps: +1. Edit replication agent +2. Transport tab +3. Enter correct username +4. Enter correct password +5. Save +6. Test Connection +``` + +**Solution B: Create/Enable User on Publish** +``` +On Publish instance: +1. Navigate to: Security → Users +2. Create user: replication-service +3. Set password (match Agent configuration) +4. Save + +Grant permissions: +1. Navigate to: Security → Permissions +2. Select user: replication-service +3. Add entries: + - Path: /content + - Privileges: jcr:read, crx:replicate, jcr:write +4. Save +``` + +**Solution C: Reset Password** +``` +On Publish instance: +1. Navigate to: Security → Users +2. Find user in agent configuration +3. Click "Set Password" +4. Enter new password +5. Save + +On Author: +1. Update replication agent with new password +2. Save +3. Test Connection +``` + +### Issue 4: SSL/TLS Certificate Errors + +**Symptoms:** +- SSL handshake failed +- Certificate validation errors +- HTTPS connection failures + +**Diagnosis:** + +1. **Check agent URI protocol:** + ``` + Agent → Transport tab + URI: https://... or http://... + ``` + +2. **Review error logs:** + ``` + error.log contains: + - javax.net.ssl.SSLHandshakeException + - PKIX path building failed + - Certificate validation failed + ``` + +**Solutions:** + +**Solution A: Enable Relaxed SSL (Development Only)** +``` +WARNING: Only for development/testing environments + +Steps: +1. Edit replication agent +2. Transport tab +3. SSL section: + ✓ Relaxed SSL (allow self-signed certificates) + ✓ Allow expired (allow expired certificates) +4. Save +5. Test Connection +``` + +**Solution B: Import Certificates (Production)** +``` +On Author instance: + +1. Export certificate from Publish: + openssl s_client -connect publish:4503 -showcerts > publish-cert.pem + +2. Import into Java keystore: + cd $JAVA_HOME/jre/lib/security + keytool -import -alias publish-aem -file publish-cert.pem \ + -keystore cacerts -storepass changeit + +3. Restart AEM Author + +4. Test replication agent connection +``` + +**Solution C: Use HTTP (Not Recommended for Production)** +``` +If SSL is not required: +1. Edit agent +2. Transport tab +3. Change URI from https:// to http:// +4. Save +5. Test Connection +``` + +### Issue 5: Content Not Appearing on Publish + +**Symptoms:** +- Replication succeeds (green status) +- Queue processes successfully +- Content still doesn't appear on Publish +- Old content served + +**Diagnosis:** + +1. **Check content directly on Publish:** + ``` + Bypass Dispatcher: + http://publish:4503/content/mysite/en/page.html + + If content appears here but not via Dispatcher: + → Dispatcher cache issue + + If content doesn't appear: + → Replication issue + ``` + +2. **Verify replication status:** + ``` + On Author: + Page → Properties → Basic tab + Check: Last Published timestamp + Verify: Status shows "Published" + ``` + +3. **Check Publish logs:** + ``` + Publish instance: crx-quickstart/logs/error.log + Search for: path of page + Look for: Errors during content import + ``` + +**Root Causes:** +- Dispatcher cache not invalidated +- Dispatcher Flush agent disabled +- Content permissions on Publish +- Sling mapping issues +- Incorrect Publish run mode configuration + +**Solutions:** + +**Solution A: Manual Dispatcher Cache Clear** +```bash +# On Dispatcher server +cd /path/to/dispatcher/cache +rm -rf * + +# Or specific path +rm -rf /path/to/dispatcher/cache/content/mysite/en/* + +# Check Dispatcher logs +tail -f /path/to/dispatcher/logs/dispatcher.log +``` + +**Solution B: Verify Dispatcher Flush Agent** +``` +On Publish instance: +1. Navigate to: Tools → Deployment → Replication +2. Select: Agents on publish +3. Click: Dispatcher Flush +4. Verify: Enabled = ✓ +5. Transport tab: + URI: http://dispatcher:80/dispatcher/invalidate.cache +6. Test Connection +7. If failed, fix connectivity +``` + +**Solution C: Check Content Permissions on Publish** +``` +On Publish instance: +1. Navigate to: CRXDE Lite +2. Browse to: /content/mysite/en/page +3. Check node exists +4. Verify permissions: anonymous user can read +5. If not, adjust permissions +``` + +**Solution D: Force Republish** +``` +On Author: +1. Select page(s) +2. Manage Publication +3. Action: Unpublish +4. Execute +5. Wait for completion +6. Manage Publication +7. Action: Publish +8. Execute +9. Verify on Publish +``` + +### Issue 6: Dispatcher Cache Not Invalidating + +**Symptoms:** +- Content published successfully +- Old content served via Dispatcher +- Direct Publish access shows new content +- Dispatcher cache files not deleted + +**Diagnosis:** + +1. **Check Dispatcher Flush agent:** + ``` + Publish instance: /etc/replication/agents.publish/flush + Status: Should be green (idle/active) + ``` + +2. **Review Dispatcher configuration:** + ``` + dispatcher.any file: + /allowedClients { + /0 { /type "allow" /glob "*publish-ip*" } + } + + /invalidate { + /0000 { /glob "*" /type "allow" } + } + ``` + +3. **Check Dispatcher logs:** + ``` + tail -f /var/log/httpd/dispatcher.log + + Look for invalidation requests: + [date] [I] [pid] Received invalidate request + ``` + +**Solutions:** + +**Solution A: Enable Dispatcher Flush Agent** +``` +On Publish instance: +1. Navigate to: /etc/replication/agents.publish/flush +2. Edit agent +3. Settings tab: ✓ Enabled +4. Serialization Type: Dispatcher Flush +5. Save +``` + +**Solution B: Fix Dispatcher Configuration** +``` +Edit dispatcher.any: + +/allowedClients { + /0 { + /type "allow" + /glob "**" + } +} + +/cache { + /invalidate { + /0000 { /glob "*" /type "allow" } + } +} + +Reload Dispatcher: +apachectl graceful +``` + +**Solution C: Verify Flush Agent Transport** +``` +Dispatcher Flush agent → Transport tab + +Correct URI format: +http://dispatcher-host:80/dispatcher/invalidate.cache + +OR if virtual host: +http://www.example.com/dispatcher/invalidate.cache + +Test Connection +``` + +### Issue 7: Timeout Errors + +**Symptoms:** +- Replication timeout errors +- Large packages fail +- Synchronous replication hangs + +**Diagnosis:** + +1. **Check agent timeouts:** + ``` + Agent → Edit → Extended tab + Connection Timeout: default 10000ms + Socket Timeout: default 10000ms + ``` + +2. **Review package size:** + ``` + Large packages (>100MB) may timeout + Check: crx-quickstart/logs/replication.log + ``` + +**Solutions:** + +**Solution A: Increase Timeouts** +``` +Agent → Edit → Extended tab + +Connection Timeout: 30000 (30 seconds) +Socket Timeout: 60000 (60 seconds) + +For very large packages: 120000 (2 minutes) +``` + +**Solution B: Use Asynchronous Replication** +``` +For large content: +1. Use default async replication (not synchronous) +2. Monitor queue instead of waiting +3. Package-based replication for very large sets +``` + +**Solution C: Split Large Packages** +``` +Instead of tree activation: +1. Activate in smaller batches +2. Use incremental replication +3. Schedule large activations during off-peak hours +``` + +### Issue 8: Replication Triggered But No Agent Found + +**Symptoms:** +- Error: "Replication triggered, but no agent found" +- Content doesn't replicate +- Logs show agent selection failure + +**Diagnosis:** + +1. **Check enabled agents:** + ``` + Navigate to: /etc/replication/agents.author + Verify: At least one agent is enabled + Check: Green status indicator + ``` + +2. **Review agent triggers:** + ``` + Agent → Edit → Triggers tab + Check: "Ignore default" is NOT checked + Verify: Appropriate triggers enabled + ``` + +**Root Causes:** +- All replication agents disabled +- All agents have "Ignore default" checked +- Agent filter excludes all agents +- Custom replication action with invalid agent ID + +**Solutions:** + +**Solution A: Enable Default Agent** +``` +Steps: +1. Navigate to: /etc/replication/agents.author/publish +2. Edit agent +3. Settings tab: ✓ Enabled +4. Triggers tab: Uncheck "Ignore default" +5. Save +``` + +**Solution B: Check Agent Filters (Programmatic)** +```java +// If using ReplicationOptions in code +ReplicationOptions opts = new ReplicationOptions(); + +// Ensure filter doesn't exclude all agents +opts.setFilter(new AgentFilter() { + public boolean isIncluded(Agent agent) { + // Return true for at least one agent + return !agent.getId().contains("invalid"); + } +}); +``` + +**Solution C: Verify Agent Configuration** +``` +For each agent: +1. Enabled: ✓ +2. Transport URI: Valid and reachable +3. Test Connection: Success +4. Triggers: At least one enabled +5. Ignore default: Unchecked (unless custom workflow) +``` + +### Issue 9: Replication Not Triggering (Blocked Servlet) + +**Symptoms:** +- Activation button works but nothing happens +- No errors shown but content doesn't replicate +- Replication queues don't populate + +**Diagnosis:** + +From official AEM 6.5 LTS documentation: Check for blocking nodes in repository. + +1. **Check for blocking nodes:** + ``` + Navigate to: CRXDE Lite (/crx/de/index.jsp) + Search for: /bin/replicate or /bin/replicate.json + + These nodes may block the replication servlet + ``` + +**Root Cause:** +Custom nodes created at `/bin/replicate` or `/bin/replicate.json` can override the default replication servlet, preventing normal replication operations. + +**Solution:** + +``` +Steps: +1. Navigate to CRXDE Lite: http://localhost:4502/crx/de/index.jsp +2. Check path: /bin/replicate +3. If node exists and is not the system servlet: + - Right-click node + - Select "Delete" + - Save All +4. Repeat for: /bin/replicate.json +5. Test replication +``` + +**Verification:** +``` +After deletion: +1. Activate a test page +2. Check replication queue processes +3. Verify content appears on Publish +``` + +### Issue 10: Namespace Replication Blocked + +**Symptoms:** +- Error: "Namespace replication failed" +- Permission denied on namespace operations +- Custom namespace content won't replicate + +**Diagnosis:** + +From official AEM 6.5 LTS documentation: Replication user lacks namespace management privileges. + +1. **Check user permissions:** + ``` + Navigate to: CRXDE Lite + Path: Repository level (/) + Check: Replication user privileges + ``` + +**Root Cause:** +The replication user (configured in agent's "Agent User Id") doesn't have `jcr:namespaceManagement` privilege, which is required to replicate custom namespaces. + +**Solution:** + +``` +Steps: +1. Navigate to CRXDE Lite +2. Select repository root: / +3. Click "Access Control" tab +4. Find replication service user +5. Add privilege: + - Privilege: jcr:namespaceManagement + - Apply +6. Save All +``` + +**Grant via CRX/DE:** +``` +1. Tools → Security → Permissions +2. Search for: replication-service user +3. Repository level permissions: + ✓ jcr:read + ✓ jcr:write + ✓ crx:replicate + ✓ jcr:namespaceManagement ← Add this +4. Save +``` + +### Issue 11: Stuck Replication Jobs in Event Queue + +**Symptoms:** +- Multiple agents blocked +- Repository appears to have replication issues +- `/var/replication/data` has many items + +**Diagnosis:** + +From official AEM 6.5 LTS documentation: Check for corrupted replication jobs. + +1. **Check event queue:** + ``` + Navigate to: CRXDE Lite + XPath Query: + /jcr:root/var/eventing/jobs//element(*,slingevent:Job) + + This shows all pending Sling event jobs + ``` + +2. **Check replication data:** + ``` + Path: /var/replication/data + Look for: Large number of nodes + ``` + +**Root Cause:** +Repository corruption or serialization errors can cause replication jobs to get stuck in the Sling event queue. + +**Solution A: Clean Event Jobs** +``` +Via CRXDE Lite: +1. Run XPath query: + /jcr:root/var/eventing/jobs//element(*,slingevent:Job) + +2. Review results for stuck jobs +3. Identify jobs with: + - Old timestamps + - Error properties + - Replication-related topic + +4. Carefully delete stuck jobs +5. Save All +``` + +**Solution B: Clear Replication Data** +``` +WARNING: Only if queue is irreparably stuck + +1. Stop AEM instance +2. Navigate to: crx-quickstart/repository/ +3. Backup: /var/replication/data +4. Delete corrupted items in /var/replication/data +5. Start AEM +6. Verify replication resumes +``` + +**Solution C: Enable Detailed Logging** + +From official documentation - configure detailed replication logging: + +``` +Navigate to: /system/console/configMgr +Search for: Apache Sling Logging Logger Configuration + +Create new configuration: +- Logger: com.day.cq.replication +- Log Level: DEBUG +- Log File: logs/replication.log + +Save and review logs for root cause +``` + +### Issue 12: Queue Pause Limitations + +**Symptoms:** +- Paused queue resumes automatically +- Pause state lost after restart + +**Diagnosis:** + +From official AEM 6.5 LTS documentation: Queue pause has known limitations. + +**Known Limitations:** + +1. **Not persisted across restarts** + - Pause state is in-memory only + - AEM restart resumes all paused queues + +2. **Auto-resume timeout** + - Idle paused queues automatically resume after ~1 hour + - Not configurable + +**Workaround:** + +Instead of pausing, disable the agent: + +``` +Agent configuration: +1. Edit agent +2. Settings tab +3. Uncheck "Enabled" +4. Save + +This persists across restarts +``` + +**For temporary pause:** +``` +Accept the limitations: +- Must re-pause after restart +- Must re-pause if idle >1 hour +- Use agent monitoring to track state +``` + +## Advanced Troubleshooting + +### Analyze Replication Logs + +**Location:** `crx-quickstart/logs/replication.log` + +**Key patterns to search:** + +```bash +# Successful replication +grep "Replication (ACTIVATE) of /content/mysite" replication.log + +# Failed replication +grep "ERROR" replication.log | grep replication + +# Agent not found +grep "no agent found" replication.log + +# Authentication failures +grep "401" replication.log + +# Connection issues +grep "Connection refused" replication.log +``` + +**Example log analysis:** +``` +# Find all replication attempts for a path +grep "/content/mysite/en/page" replication.log + +# Count failures by type +grep "ERROR" replication.log | cut -d' ' -f5- | sort | uniq -c | sort -rn + +# Recent replication activity +tail -100 replication.log | grep "ACTIVATE\|DEACTIVATE" +``` + +### Monitor via JMX Console + +``` +Navigate to: /system/console/jmx + +Search for: com.day.cq.replication + +Monitor MBeans: +- Replication Agent Stats + - Queue Size + - Number of queued items + - Last processed item + - Error count + +- Replication Service + - Active replications + - Failed replications + - Average processing time +``` + +### Check OSGi Configuration + +``` +Navigate to: /system/console/configMgr + +Relevant configurations: +- Day CQ Replication Service +- Day CQ WCM Replication Impl ReplicationComponentFactoryImpl +- Apache Sling Job Consumer Manager + +Verify: +- Services are active +- No configuration errors +- Thread pools not exhausted +``` + +### Examine Event Queue + +``` +Navigate to: /system/console/slingevent + +Check: +- Event queue depth +- Stuck events +- Processing rate +- Failed events + +If queue stuck: +- Restart org.apache.sling.event bundle +- Check disk space +- Review thread dumps +``` + +## Preventive Measures + +### 1. Regular Agent Testing + +Schedule periodic tests: +``` +Weekly: +1. Test Connection for all agents +2. Verify queues are empty +3. Review error logs +4. Check disk space on Publish +``` + +### 2. Monitoring and Alerts + +Set up monitoring for: +- Replication queue depth > 100 +- Agent blocked > 5 minutes +- Replication failures > 10 per hour +- Disk space < 10% free +- High replication lag (>5 minutes) + +### 3. Maintenance Tasks + +Regular maintenance: +``` +Monthly: +- Review and clear old logs +- Verify agent credentials +- Test disaster recovery procedures +- Update documentation + +Quarterly: +- Certificate renewal checks +- Performance testing +- Capacity planning review +``` + +### 4. Best Practices + +- **Use dedicated service accounts** for replication (not admin) +- **Implement monitoring** for queue depth and errors +- **Test agent connectivity** after configuration changes +- **Document custom agents** and their purpose +- **Schedule large activations** during off-peak hours +- **Keep Publish instances in sync** with same configuration +- **Regular log review** to catch issues early +- **Maintain runbooks** for common issues + +## Diagnostic Checklist + +Use this checklist for systematic troubleshooting: + +``` +□ Verify symptom and impact +□ Check replication agent status (green/red) +□ Review replication queue for stuck items +□ Test agent connectivity +□ Verify Publish instance is running +□ Check authentication credentials +□ Review error.log and replication.log +□ Verify agent configuration (URI, credentials, settings) +□ Check network connectivity (ping, telnet, curl) +□ Test direct Publish access (bypass Dispatcher) +□ Verify Dispatcher Flush agent (if applicable) +□ Check content permissions on Publish +□ Review OSGi bundles status +□ Examine Sling event queue +□ Check disk space on Author and Publish +□ Verify JVM heap usage +□ Test with simple content first +□ Document findings and resolution +``` + +## Escalation Path + +If issue persists after troubleshooting: + +1. **Gather diagnostic information:** + - Exact error messages + - Replication.log excerpts + - Agent configuration screenshots + - Steps to reproduce + - Environment details (AEM version, OS, Java version) + +2. **Check Adobe Experience League Community:** + - Search for similar issues + - Post detailed question with diagnostics + +3. **Adobe Support (if entitled):** + - Open support ticket + - Provide thread dumps if hanging + - Share log bundles + - Include replication queue screenshots + +## Related Skills + +- `configure-replication-agent`: Set up and configure agents properly +- `replicate-content`: Understand replication methods +- `replication-api`: Programmatic replication for custom code + +## Success Criteria + +- ✓ Replication agents showing green status (idle/active) +- ✓ Replication queues empty or processing normally +- ✓ Test Connection succeeds for all agents +- ✓ Content appears on Publish after activation +- ✓ Dispatcher cache invalidates properly +- ✓ No errors in replication.log or error.log +- ✓ Replication status shows "Published" on Author +- ✓ Performance is acceptable (activation <2 minutes) +- ✓ Root cause identified and documented +- ✓ Preventive measures implemented + +## Additional Resources + +- [Official AEM 6.5 LTS Replication Documentation](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/replication) +- [AEM 6.5 LTS Replication Troubleshooting Guide](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts/content/implementing/deploying/configuring/troubleshoot-rep) +- [AEM 6.5 LTS Documentation Hub](https://experienceleague.adobe.com/en/docs/experience-manager-65-lts) +- [Replication API JavaDoc (Package Summary)](https://developer.adobe.com/experience-manager/reference-materials/6-5-lts/javadoc/com/day/cq/replication/package-summary.html) +- [AEM Replication Cookbook (Community)](https://aemlounge.wordpress.com/2018/03/19/a-cookbook-for-replication-in-aem/) +- Adobe Experience League Community Forums +- AEM Operations Dashboard documentation