<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AEM Deep Dive]]></title><description><![CDATA[A deep dive into AEM Sites, Assets, and everything in between — written from years of hands-on implementation experience.]]></description><link>https://aemdeepdive.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6aa50484cfdba5da96ef4808/1d1c80a1-2f4c-47c5-929f-c6e29f2f08ff.webp</url><title>AEM Deep Dive</title><link>https://aemdeepdive.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 19:32:40 GMT</lastBuildDate><atom:link href="https://aemdeepdive.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[AEM Project Structure Explained]]></title><description><![CDATA[Series: AEM Foundations — Part 5
When you generated a project from Adobe's Maven archetype earlier in this series, it created several modules with cryptic-looking names — core, ui.apps, ui.content, ui]]></description><link>https://aemdeepdive.hashnode.dev/aem-project-structure-explained</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/aem-project-structure-explained</guid><category><![CDATA[AEM]]></category><category><![CDATA[AEM implementation]]></category><category><![CDATA[AEM Tutorial]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:32:59 GMT</pubDate><content:encoded><![CDATA[<p><em>Series: AEM Foundations — Part 5</em></p>
<p>When you generated a project from Adobe's Maven archetype earlier in this series, it created several modules with cryptic-looking names — <code>core</code>, <code>ui.apps</code>, <code>ui.content</code>, <code>ui.config</code>, and more. This post breaks down what each module is for and why AEM projects are structured this way.</p>
<h2>Why AEM Projects Are Multi-Module</h2>
<p>Unlike a typical web app where everything might live in one deployable unit, AEM separates <strong>code</strong>, <strong>content</strong>, and <strong>configuration</strong> into distinct Maven modules. This separation exists because these three things have very different lifecycles:</p>
<ul>
<li><p>Code changes with every feature or bug fix</p>
</li>
<li><p>Content (sample pages, component defaults) is set up once and rarely redeployed</p>
</li>
<li><p>Configuration (OSGi settings) often differs between environments (dev, stage, prod)</p>
</li>
</ul>
<p>Keeping them separate means you can deploy code changes without accidentally overwriting content, and manage environment-specific configuration independently.</p>
<h2>The Core Modules</h2>
<h3><code>core</code></h3>
<p>This is where your <strong>Java code</strong> lives — Sling Models, Servlets, OSGi services, schedulers, and any custom business logic. If you're writing Java for AEM, this is almost always where it goes. It compiles into an OSGi bundle that gets deployed into AEM's runtime.</p>
<h3><code>ui.apps</code></h3>
<p>This holds everything that goes under <code>/apps</code> in the JCR repository:</p>
<ul>
<li><p>Component definitions (dialogs, HTL scripts, <code>.content.xml</code> files)</p>
</li>
<li><p>Client libraries (CSS/JS, bundled as <code>clientlibs</code>)</p>
</li>
<li><p>Templates and template policies</p>
</li>
<li><p>Any other application-level structure</p>
</li>
</ul>
<p>Recall from the previous post in this series: <code>/apps</code> is where you place your customizations, which Sling looks up before falling back to Adobe's defaults under <code>/libs</code>. This module is effectively "your app's definition" in the repository.</p>
<h3><code>ui.content</code></h3>
<p>This contains <strong>sample or default content</strong> — example pages, folder structures under <code>/content</code>, and sometimes reference content used for demos or initial setup. In many real projects, this module is either minimal or excluded from production deployments entirely, since live content is authored directly in the Author environment rather than deployed via code.</p>
<h3><code>ui.config</code></h3>
<p>This holds <strong>OSGi configuration</strong> — settings for things like PDF processing, workflow launchers, or any configurable service, often split by run mode (author vs. publish, dev vs. prod) so the same codebase behaves correctly across environments.</p>
<h3><code>ui.frontend</code> (in newer archetype versions)</h3>
<p>Newer AEM projects often include a dedicated frontend module using modern tooling (webpack, npm) to build client-side assets, which then get packaged into <code>ui.apps</code> clientlibs during the build. This lets front-end developers work with familiar JS/CSS tooling rather than hand-writing clientlib folder structures.</p>
<h3><code>it.tests</code> (optional)</h3>
<p>Some projects include an integration test module here, used for automated tests that run against a deployed AEM instance as part of CI/CD pipelines.</p>
<h3><code>all</code></h3>
<p>This is a "wrapper" package that bundles <code>core</code>, <code>ui.apps</code>, <code>ui.content</code>, and <code>ui.config</code> together into a single deployable content package. When you ran <code>mvn clean install -PautoInstallSinglePackage</code> in the previous post, this is typically the package that actually got installed onto your local instance.</p>
<h2>A Simplified Visual</h2>
<pre><code class="language-plaintext">my-project/
├── core/            → Java code (Sling Models, Servlets, OSGi services)
├── ui.apps/         → /apps content: components, clientlibs, templates
├── ui.content/      → /content sample or reference content
├── ui.config/       → OSGi configurations per run mode
├── ui.frontend/     → Modern frontend build tooling (if present)
├── it.tests/        → Integration tests (optional)
└── all/             → Combined deployable package
</code></pre>
<h2>Why This Structure Pays Off Long-Term</h2>
<p>This separation might feel like overhead on a small project, but it becomes essential as a project grows:</p>
<ul>
<li><p>Multiple developers can work on <code>core</code> (Java logic) and <code>ui.apps</code> (components/front-end) simultaneously with minimal conflict</p>
</li>
<li><p>Environment-specific behavior lives cleanly in <code>ui.config</code>, instead of scattered <code>if (environment == "prod")</code> checks in code</p>
</li>
<li><p>CI/CD pipelines can build and deploy the <code>all</code> package as a single unit, while still keeping the underlying modules independently testable</p>
</li>
</ul>
<h2>Series Wrap-Up</h2>
<p>That completes the <strong>AEM Foundations</strong> series. At this point you should understand:</p>
<ul>
<li><p>What AEM actually is, and how Sites, Assets, and Forms differ</p>
</li>
<li><p>The Author/Publish/Dispatcher architecture and how requests flow through it</p>
</li>
<li><p>How to set up a local AEM SDK environment</p>
</li>
<li><p>How Sling resolves resources and processes requests</p>
</li>
<li><p>How a real AEM project is organized on disk</p>
</li>
</ul>
<p>From here, the <strong>AEM Core Development</strong> series picks up with hands-on component building — starting with your first HTL component and Sling Model.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Sling: Resource Resolution & Request Lifecycle]]></title><description><![CDATA[Series: AEM Foundations — Part 4
AEM is built on top of Apache Sling, a web framework that takes a fundamentally different approach to handling requests than typical Java web frameworks. If you've wor]]></description><link>https://aemdeepdive.hashnode.dev/understanding-sling-resource-resolution-request-lifecycle</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/understanding-sling-resource-resolution-request-lifecycle</guid><category><![CDATA[AEM]]></category><category><![CDATA[AEM Tutorial]]></category><category><![CDATA[Sling]]></category><category><![CDATA[AEM implementation]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:32:09 GMT</pubDate><content:encoded><![CDATA[<p><em>Series: AEM Foundations — Part 4</em></p>
<p>AEM is built on top of <strong>Apache Sling</strong>, a web framework that takes a fundamentally different approach to handling requests than typical Java web frameworks. If you've worked with Spring MVC or a similar framework, Sling's model will initially feel unusual — there's no central routing table mapping URLs to controllers. Instead, everything revolves around <strong>resources</strong>.</p>
<h2>Everything Is a Resource</h2>
<p>In Sling, a "resource" is any node in the JCR repository — a page, a component instance, an asset, a piece of content. Every resource has a <strong>path</strong> and a <strong>resource type</strong>, and this pairing is the foundation of how Sling decides what to render.</p>
<p>For example, a page might live at:</p>
<pre><code class="language-plaintext">/content/mysite/en/home
</code></pre>
<p>And a component instance on that page might be:</p>
<pre><code class="language-plaintext">/content/mysite/en/home/jcr:content/root/container/text
</code></pre>
<p>That text component's <code>sling:resourceType</code> property might point to something like <code>mysite/components/text</code>, which tells Sling exactly which script or Java class should render it.</p>
<h2>Resource Resolution: How a URL Becomes Content</h2>
<p>When a request comes in, Sling doesn't look up a controller — it resolves the <strong>resource</strong> at that URL path first, then figures out how to render it. The core steps are:</p>
<ol>
<li><p><strong>Strip the request URL</strong> down to a repository path (handling extensions and selectors along the way)</p>
</li>
<li><p><strong>Resolve the resource</strong> at that path in the JCR</p>
</li>
<li><p>Determine the resource's <code>sling:resourceType</code></p>
</li>
<li><p><strong>Locate a matching script or Servlet/Sling Model</strong> for that resource type, searching in the <code>/apps</code> overlay area first, then falling back to <code>/libs</code></p>
</li>
<li><p><strong>Render</strong> using that script</p>
</li>
</ol>
<p>This <code>/apps</code> overlay-first, <code>/libs</code> fallback pattern is core to how AEM customization works — Adobe ships default implementations under <code>/libs</code>, and you customize behavior by placing your own resource type definitions under <code>/apps</code> without ever touching Adobe's shipped code.</p>
<h2>Selectors and Extensions</h2>
<p>Sling URLs can carry extra information beyond just the path, using <strong>selectors</strong> and <strong>extensions</strong>. A URL like:</p>
<pre><code class="language-plaintext">/content/mysite/en/home.json
</code></pre>
<p>requests the JSON rendering of that resource, while:</p>
<pre><code class="language-plaintext">/content/mysite/en/home.print.html
</code></pre>
<p>uses the <code>print</code> selector to request an alternate HTML rendering — useful for rendering the same content differently depending on context (a print-friendly layout, a mobile-specific variant, an API-style JSON output) without duplicating content.</p>
<h2>The Sling Request Lifecycle, Step by Step</h2>
<p>Putting it together, here's roughly what happens when a browser requests an AEM page:</p>
<ol>
<li><p>The request hits the <strong>Dispatcher</strong> (if it's a live/Publish request) — see the Architecture post in this series</p>
</li>
<li><p>If not cached, it reaches the <strong>Sling engine</strong> inside AEM</p>
</li>
<li><p>Sling's <strong>Resource Resolver</strong> maps the URL to a JCR resource path</p>
</li>
<li><p>Sling determines the resource's <strong>type</strong>, selectors, and extension</p>
</li>
<li><p>It locates the appropriate <strong>rendering script</strong> (HTL/JSP) or <strong>Servlet</strong></p>
</li>
<li><p>If the component uses a <strong>Sling Model</strong>, Sling instantiates it and injects data from the resource (we'll cover this in detail in the next series)</p>
</li>
<li><p>The script renders using that data, producing the final HTML/JSON/etc. response</p>
</li>
<li><p>The response is sent back — and cached by Dispatcher if applicable</p>
</li>
</ol>
<h2>Why This Model Matters</h2>
<p>Once resource resolution clicks, a lot of AEM customization patterns make immediate sense:</p>
<ul>
<li><p><strong>Overlaying</strong> Adobe's default components just means creating a matching path under <code>/apps</code> — no core code modification needed</p>
</li>
<li><p><strong>Multi-site, multi-language setups</strong> work because the same resource type can be reused across different content paths</p>
</li>
<li><p>Debugging "why isn't my component rendering the way I expect" almost always comes down to checking the resource's actual <code>sling:resourceType</code> and where the resolver is finding (or failing to find) a matching script</p>
</li>
</ul>
<h2>What's Next</h2>
<p>With resource resolution and the request lifecycle covered, the final post in this series looks at how a real AEM project is organized on disk — the <code>core</code>, <code>ui.apps</code>, <code>ui.content</code>, and other modules you saw generated by the archetype in the previous post.</p>
<hr />
<p><em>Next in this series: [AEM Project Structure Explained]</em></p>
]]></content:encoded></item><item><title><![CDATA[Setting Up a Local AEM SDK Dev Environment]]></title><description><![CDATA[Series: AEM Foundations — Part 3
Before you can build components, write Sling Models, or test anything discussed in this series, you need a working local AEM environment. This post walks through getti]]></description><link>https://aemdeepdive.hashnode.dev/setting-up-a-local-aem-sdk-dev-environment</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/setting-up-a-local-aem-sdk-dev-environment</guid><category><![CDATA[AEM]]></category><category><![CDATA[aemsetup]]></category><category><![CDATA[AEM Tutorial]]></category><category><![CDATA[AEM implementation]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:27:29 GMT</pubDate><content:encoded><![CDATA[<p><em>Series: AEM Foundations — Part 3</em></p>
<p>Before you can build components, write Sling Models, or test anything discussed in this series, you need a working local AEM environment. This post walks through getting AEM as a Cloud Service SDK running on your machine.</p>
<h2>Prerequisites</h2>
<p>Make sure you have these installed before starting:</p>
<ul>
<li><p><strong>Java Development Kit (JDK)</strong> — AEM as a Cloud Service SDK currently requires a specific supported JDK version (check Adobe's release notes for the exact version, as this changes between SDK releases)</p>
</li>
<li><p><strong>Apache Maven</strong> — for building and deploying your project code</p>
</li>
<li><p><strong>An IDE</strong> — IntelliJ IDEA or Eclipse (with AEM-specific plugins) are both common choices</p>
</li>
<li><p><strong>Adobe I/O access</strong> — you'll need an Adobe account with access to the Software Distribution portal to download the SDK</p>
</li>
</ul>
<h2>Step 1: Download the AEM SDK</h2>
<ol>
<li><p>Log in to the <a href="https://experience.adobe.com/#/downloads">Adobe Software Distribution portal</a></p>
</li>
<li><p>Locate <strong>AEM as a Cloud Service SDK</strong></p>
</li>
<li><p>Download the latest <strong>QuickStart JAR</strong></p>
</li>
</ol>
<p>Adobe ships new SDK versions frequently, so it's worth checking for the latest release rather than reusing an old download, especially if you're following along with current documentation.</p>
<h2>Step 2: Run the Author Instance</h2>
<p>Create a dedicated folder for your local setup, place the SDK jar inside it, then run:</p>
<pre><code class="language-bash">java -jar aem-sdk-quickstart-*.jar
</code></pre>
<p>By default, this starts an <strong>Author</strong> instance on port <code>4502</code>. The first run will take a few minutes as AEM unpacks and initializes its repository. Once it's up, you can log in at:</p>
<pre><code class="language-plaintext">http://localhost:4502
</code></pre>
<p>Default credentials are <code>admin</code> / <code>admin</code> — change this immediately in any environment beyond your personal sandbox.</p>
<h2>Step 3: Run the Publish Instance</h2>
<p>To run a Publish instance alongside Author, start the jar with the <code>publish</code> run mode and a different port:</p>
<pre><code class="language-bash">java -jar aem-sdk-quickstart-*.jar -r publish -p 4503
</code></pre>
<p>This lets you test the full Author → Publish flow locally, including replication, without needing a second machine.</p>
<h2>Step 4: Set Up Your Project Structure</h2>
<p>Rather than building a project from scratch, Adobe provides an <strong>archetype</strong> — a Maven template that scaffolds a standard AEM project structure for you:</p>
<pre><code class="language-bash">mvn -B org.apache.maven.plugins:maven-archetype-plugin:3.2.1:generate \
  -D archetypeGroupId=com.adobe.aem \
  -D archetypeArtifactId=aem-project-archetype \
  -D archetypeVersion=&lt;latest-version&gt; \
  -D appTitle="My AEM Project" \
  -D appId="myproject" \
  -D groupId="com.mycompany.myproject"
</code></pre>
<p>This generates a multi-module Maven project (<code>core</code>, <code>ui.apps</code>, <code>ui.content</code>, <code>ui.config</code>, and more) that follows Adobe's recommended conventions — we'll break this structure down in detail in the next post.</p>
<h2>Step 5: Build and Deploy Your Project</h2>
<p>From your generated project's root directory, deploy everything to your local Author instance with:</p>
<pre><code class="language-bash">mvn clean install -PautoInstallSinglePackage
</code></pre>
<p>If this completes without errors, your custom project code (even if it's just the archetype's sample content at this point) is now live on your local AEM instance.</p>
<h2>Common First-Time Issues</h2>
<p>A few things that trip up almost everyone on their first setup:</p>
<ul>
<li><p><strong>Port conflicts</strong> — if 4502 or 4503 are already in use, specify a different port with <code>-p &lt;port&gt;</code> when starting the jar</p>
</li>
<li><p><strong>Insufficient memory</strong> — AEM is resource-hungry; make sure your machine has enough RAM allocated, and consider increasing JVM heap size if you see out-of-memory errors during startup</p>
</li>
<li><p><strong>JDK version mismatches</strong> — using an unsupported JDK version is one of the most common causes of startup failures; always check the SDK release notes for the required version</p>
</li>
<li><p><strong>Maven build failures</strong> — usually caused by an outdated archetype version or a misconfigured <code>settings.xml</code> for Maven repositories</p>
</li>
</ul>
<h2>What's Next</h2>
<p>With a working local environment, you're ready to understand what's actually happening under the hood when AEM processes a request. The next post covers <strong>Apache Sling</strong> — the framework AEM is built on — and how resource resolution and the request lifecycle work.</p>
<hr />
<p><em>Next in this series: [Understanding Sling: Resource Resolution &amp; Request Lifecycle]</em></p>
]]></content:encoded></item><item><title><![CDATA[AEM Architecture Explained: Author, Publish, Dispatcher]]></title><description><![CDATA[Series: AEM Foundations — Part 2
If there's one concept every AEM developer needs to internalize before writing code, it's the three-tier architecture: Author, Publish, and Dispatcher. Misunderstandin]]></description><link>https://aemdeepdive.hashnode.dev/aem-architecture-explained-author-publish-dispatcher</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/aem-architecture-explained-author-publish-dispatcher</guid><category><![CDATA[AEM]]></category><category><![CDATA[AEM Tutorial]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:26:03 GMT</pubDate><content:encoded><![CDATA[<p><em>Series: AEM Foundations — Part 2</em></p>
<p>If there's one concept every AEM developer needs to internalize before writing code, it's the three-tier architecture: <strong>Author</strong>, <strong>Publish</strong>, and <strong>Dispatcher</strong>. Misunderstanding this leads to some of the most common (and confusing) bugs new AEM developers hit — content that "works on author but not on publish," caching issues, or components that behave inconsistently.</p>
<h2>The Author Instance</h2>
<p>The Author instance is where content authors and developers work. Think of it as the "backstage" environment:</p>
<ul>
<li><p>Authors log in, create pages, drag components onto templates, and configure content</p>
</li>
<li><p>Developers deploy code here first to test</p>
</li>
<li><p>It has the full authoring UI: the page editor, component dialogs, the DAM console, workflow tools</p>
</li>
<li><p>It is <strong>never exposed directly to end users</strong> — it should sit behind your organization's firewall or VPN</p>
</li>
</ul>
<p>Because Author holds the editing tools and unpublished drafts, it's also where content review and approval workflows typically run before anything goes live.</p>
<h2>The Publish Instance</h2>
<p>The Publish instance is what actually serves content to real visitors. Key differences from Author:</p>
<ul>
<li><p>It's a <strong>read-optimized</strong> copy of content, replicated from Author</p>
</li>
<li><p>It has no authoring UI — it just renders pages</p>
</li>
<li><p>In production, you'll usually run <strong>multiple Publish instances</strong> behind a load balancer for scalability and redundancy</p>
</li>
<li><p>Content reaches Publish through a process called <strong>replication</strong> — when an author clicks "Publish" on a page, that content is pushed from Author to one or more Publish instances</p>
</li>
</ul>
<p>This separation exists for a good reason: it keeps your live site fast and stable, since Publish doesn't carry the overhead of the authoring tools, and it means authors can safely draft and preview content without it being visible to the public.</p>
<h2>The Dispatcher</h2>
<p>The Dispatcher is often the most misunderstood piece for newcomers. It's <strong>not a full AEM instance</strong> — it's a caching and load-balancing module (technically an Apache HTTP Server module) that sits in front of Publish.</p>
<p>Its main jobs:</p>
<ul>
<li><p><strong>Caching</strong> rendered HTML pages so Publish doesn't have to regenerate them on every request</p>
</li>
<li><p><strong>Security filtering</strong> — blocking requests to sensitive paths, restricting what URL patterns are servable</p>
</li>
<li><p><strong>Load balancing</strong> across multiple Publish instances</p>
</li>
</ul>
<p>When someone visits your live AEM site, their request usually hits the Dispatcher first. If a cached version of that page exists and is still valid, Dispatcher serves it directly — Publish never even sees the request. If there's no valid cache, Dispatcher forwards the request to Publish, caches the response, and serves it.</p>
<p>This is why cache invalidation is such a recurring topic in AEM — if Dispatcher is serving a stale cached page after a content update, visitors won't see the change until the cache is cleared or refreshed (we'll cover this in depth in the Architecture &amp; Operations series).</p>
<h2>Putting It Together: A Request's Journey</h2>
<p>Here's the typical flow when a page is published and then visited:</p>
<ol>
<li><p>An author edits a page on <strong>Author</strong> and clicks Publish</p>
</li>
<li><p><strong>Replication</strong> pushes that content to the <strong>Publish</strong> instance(s)</p>
</li>
<li><p>A visitor requests the page URL</p>
</li>
<li><p>The request hits <strong>Dispatcher</strong> first</p>
</li>
<li><p>Dispatcher checks its cache — serves the cached file if valid, or forwards to <strong>Publish</strong> if not</p>
</li>
<li><p>Publish renders the page (using your Java/HTL components) and returns it</p>
</li>
<li><p>Dispatcher caches the response for future requests and serves it to the visitor</p>
</li>
</ol>
<h2>Why This Matters Day-to-Day</h2>
<p>Once this model clicks, a lot of AEM "mysteries" stop being mysterious:</p>
<ul>
<li><p>A page looking wrong on the live site but fine in Author almost always points to a <strong>replication</strong> or <strong>caching</strong> issue, not a code bug</p>
</li>
<li><p>Performance problems on a live site are often a <strong>Dispatcher caching</strong> configuration issue, not a Java/HTL problem</p>
</li>
<li><p>Security reviews frequently focus on Dispatcher rules, since it's your first line of defense against unwanted requests reaching Publish</p>
</li>
</ul>
<h2>What's Next</h2>
<p>Now that you know how requests flow through AEM's architecture, the next post sets up your actual <strong>local development environment</strong> using the AEM SDK, so you can start building and testing components on your own machine.</p>
<hr />
<p><em>Next in this series: [Setting Up a Local AEM SDK Dev Environment]</em></p>
]]></content:encoded></item><item><title><![CDATA[What is AEM? Sites & Assets — a practical overview]]></title><description><![CDATA[Series: AEM Foundations — Part 1
If you're new to Adobe Experience Manager (AEM), the first hurdle isn't the code — it's understanding what AEM actually is. The name gets used loosely to mean several ]]></description><link>https://aemdeepdive.hashnode.dev/what-is-aem-practical-overview</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/what-is-aem-practical-overview</guid><category><![CDATA[AEM]]></category><category><![CDATA[AEM Tutorial]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:24:10 GMT</pubDate><content:encoded><![CDATA[<p><em>Series: AEM Foundations — Part 1</em></p>
<p>If you're new to Adobe Experience Manager (AEM), the first hurdle isn't the code — it's understanding what AEM actually <em>is</em>. The name gets used loosely to mean several different products bundled under one platform, and that ambiguity trips up a lot of newcomers. Let's clear it up.</p>
<h2>AEM Is a Suite, Not a Single Product</h2>
<p>Adobe Experience Manager is part of the Adobe Experience Cloud, and it's really an umbrella term for three major capabilities:</p>
<ul>
<li><p><strong>AEM Sites</strong> — a content management system (CMS) for building, managing, and personalizing websites</p>
</li>
<li><p><strong>AEM Assets</strong> — a digital asset management (DAM) system for storing, organizing, and delivering media at scale</p>
</li>
<li><p><strong>AEM Forms</strong> — a tool for building and managing digital forms and documents, often used in regulated industries (insurance, banking, healthcare)</p>
</li>
</ul>
<p>You can license and use these independently, or together, depending on what your organization needs. Most AEM developers start with Sites, since it's the most commonly implemented piece, but larger enterprises often run Sites and Assets together — a marketing team publishing pages while a separate creative team manages a shared media library feeding those pages.</p>
<h2>AEM Sites: The CMS Layer</h2>
<p>AEM Sites lets content authors build and edit web pages using a component-based system, without needing to touch code. As a developer, your job is to build the components (things like banners, carousels, text blocks, forms) that authors can drag onto a page and configure through dialogs.</p>
<p>Key things that make AEM Sites distinct from a typical CMS like WordPress:</p>
<ul>
<li><p>It's built on <strong>Apache Sling</strong> and the <strong>Java Content Repository (JCR)</strong>, not a relational database</p>
</li>
<li><p>Content is structured as a <strong>tree of nodes</strong>, which gives it enormous flexibility for multi-site, multi-language setups</p>
</li>
<li><p>It supports <strong>component-driven authoring</strong> — every visible piece of a page is a reusable, configurable component</p>
</li>
<li><p>Publishing is separated from authoring through distinct <strong>Author</strong> and <strong>Publish</strong> environments (more on this in the next post)</p>
</li>
</ul>
<h2>AEM Assets: The DAM Layer</h2>
<p>AEM Assets is where images, videos, PDFs, and other media live. It's far more than a file storage system — it includes:</p>
<ul>
<li><p>Automated <strong>rendition generation</strong> (resizing, format conversion, cropping)</p>
</li>
<li><p><strong>Metadata management</strong> and tagging, increasingly automated with AI-based Smart Tags</p>
</li>
<li><p><strong>Workflow automation</strong> for asset approval, review, and processing pipelines</p>
</li>
<li><p>Integration points so Sites components can pull assets directly from the DAM rather than duplicating files</p>
</li>
</ul>
<p>If you're coming from a pure web development background, Assets can feel like a different discipline entirely — it's as much about digital operations and metadata governance as it is about code.</p>
<h2>AEM Forms: The Overlooked Third Pillar</h2>
<p>AEM Forms doesn't get as much attention in tutorials, but it's a serious product used heavily in industries with complex document workflows. It handles:</p>
<ul>
<li><p>Adaptive forms that render responsively across devices</p>
</li>
<li><p>Document generation (e.g., auto-filled PDF contracts)</p>
</li>
<li><p>Digital signature integrations</p>
</li>
<li><p>Complex multi-step form logic for things like insurance claims or loan applications</p>
</li>
</ul>
<p>If your career path is heading toward enterprise or regulated-industry AEM work, it's worth knowing Forms exists even if your first project doesn't touch it.</p>
<h2>Why This Matters for Your Learning Path</h2>
<p>Understanding this split early saves you from a common beginner mistake: assuming every AEM tutorial applies to your situation. A Sling Model tutorial for a Sites component won't help you configure an Assets workflow, and an Adaptive Forms rule editor has almost nothing in common with either.</p>
<p>For this blog, we'll mostly focus on <strong>AEM Sites and AEM Assets</strong>, since that combination covers the vast majority of real-world AEM development work.</p>
<h2>What's Next</h2>
<p>In the next post, we'll break down how AEM Sites actually runs under the hood — the Author instance, Publish instance, and Dispatcher — and why understanding this separation is critical before you write a single line of component code.</p>
<hr />
<p><em>Next in this series: [AEM Architecture Explained: Author, Publish, and Dispatcher]</em></p>
]]></content:encoded></item><item><title><![CDATA[Setting Up AEM with Dispatcher on Apache 2.2 (Local Development Environment)]]></title><description><![CDATA[If you're working on AEM Sites or Assets projects, sooner or later you'll need to test caching, URL rewriting, or security filters the way they'll actually behave in production — and that means runnin]]></description><link>https://aemdeepdive.hashnode.dev/setting-up-aem-with-dispatcher-on-apache-2-2</link><guid isPermaLink="true">https://aemdeepdive.hashnode.dev/setting-up-aem-with-dispatcher-on-apache-2-2</guid><category><![CDATA[AEM]]></category><category><![CDATA[dispatcher]]></category><dc:creator><![CDATA[MadhusudhanG]]></dc:creator><pubDate>Sat, 12 Sep 2026 13:15:34 GMT</pubDate><content:encoded><![CDATA[<p>If you're working on AEM Sites or Assets projects, sooner or later you'll need to test caching, URL rewriting, or security filters the way they'll actually behave in production — and that means running a <strong>Dispatcher</strong> in front of your AEM instance, even locally.</p>
<p>This guide walks through setting up the AEM Dispatcher module with <strong>Apache HTTP Server 2.2</strong> on your local machine.</p>
<blockquote>
<p><strong>Note:</strong> Apache 2.2 reached end-of-life a while back, and most current AEM Dispatcher releases (5.x) are built and tested against Apache 2.4. If you have a choice, prefer Apache 2.4 for new setups. This guide is for cases where you specifically need to replicate an existing 2.2-based environment (e.g., matching a legacy production stack).</p>
</blockquote>
<h2>Prerequisites</h2>
<p>Before you start, make sure you have:</p>
<ul>
<li><p>A running local AEM instance (author and/or publish) — typically on port <code>4502</code> (author) and <code>4503</code> (publish)</p>
</li>
<li><p>Apache HTTP Server 2.2 installed</p>
</li>
<li><p>The AEM Dispatcher module package for Apache 2.2 (downloaded from Adobe Software Distribution — you'll need a valid Adobe account with AEM entitlements)</p>
</li>
<li><p>Basic familiarity with editing <code>httpd.conf</code></p>
</li>
</ul>
<h2>Step 1: Verify Your Apache Installation</h2>
<p>Confirm Apache 2.2 is installed and check whether it's 32-bit or 64-bit, since the Dispatcher module must match:</p>
<pre><code class="language-bash">httpd -v
httpd -V | grep -i "architecture"
</code></pre>
<p>You'll need this info to pick the correct Dispatcher binary in the next step.</p>
<h2>Step 2: Download and Place the Dispatcher Module</h2>
<p>From the Dispatcher package you downloaded, locate the module file matching your OS and Apache version:</p>
<ul>
<li><p>Windows: <code>dispatcher-apache2.2-x86_64-&lt;version&gt;.so</code> (or 32-bit equivalent ex: <strong>disp_apache2.2.dll</strong>)</p>
</li>
<li><p>Linux: <code>dispatcher-apache2.2-x86_64-&lt;version&gt;.so</code></p>
</li>
<li><p>macOS: <code>dispatcher-apache2.2-x86_64-&lt;version&gt;.so</code></p>
</li>
</ul>
<p>Copy this file into your Apache <code>modules</code> directory and rename it for clarity:</p>
<pre><code class="language-bash">cp dispatcher-apache2.2-x86_64-&lt;version&gt;.so /path/to/apache2.2/modules/mod_dispatcher.so
</code></pre>
<h2>Step 3: Load the Module in httpd.conf</h2>
<p>Open your Apache configuration file (<code>conf/httpd.conf</code>) and add the following line, ideally near the other <code>LoadModule</code> directives:</p>
<pre><code class="language-apache">LoadModule dispatcher_module modules/mod_dispatcher.so
</code></pre>
<h2>Step 4: Create the Dispatcher Configuration Directory</h2>
<p>Create a folder to hold your Dispatcher configuration files, e.g.:</p>
<pre><code class="language-bash">mkdir /path/to/apache2.2/conf/dispatcher
</code></pre>
<p>Inside this folder, you'll typically create:</p>
<ul>
<li><p><code>dispatcher.any</code> — main dispatcher config</p>
</li>
<li><p><code>available_farms/</code> — folder holding one <code>.any</code> file per "farm" (site)</p>
</li>
<li><p><code>cache/</code> — local disk cache directory used by Dispatcher</p>
</li>
</ul>
<h2>Step 5: Configure the Dispatcher Module in httpd.conf</h2>
<p>Add the following block to <code>httpd.conf</code> to point Apache at your Dispatcher config and enable the handler:</p>
<pre><code class="language-apache">&lt;IfModule disp_apache2.c&gt;
    DispatcherConfig conf/dispatcher/dispatcher.any
    DispatcherLog logs/dispatcher.log
    DispatcherLogLevel 3
    DispatcherNoServerHeader Off
    DispatcherDeclineRoot Off
    DispatcherUseProcessedURL Off
    DispatcherPassError 0
&lt;/IfModule&gt;

&lt;LocationMatch "^/(.*)"&gt;
    SetHandler dispatcher-handler
&lt;/LocationMatch&gt;
</code></pre>
<h2>Step 6: Create the Main dispatcher.any File</h2>
<p>Inside <code>conf/dispatcher/dispatcher.any</code>, reference your farm file(s):</p>
<pre><code class="language-plaintext">/farms
{
  $include "available_farms/*.any"
}
</code></pre>
<h2>Step 7: Create a Farm Configuration</h2>
<p>Create <code>available_farms/localhost.any</code> with a basic farm definition pointing to your local AEM publish instance:</p>
<pre><code class="language-plaintext">/localhost
{
  /clientheaders
  {
    "*"
  }

  /virtualhosts
  {
    "localhost"
  }

  /renders
  {
    /rend01
    {
      /hostname "localhost"
      /port "4503"
    }
  }

  /filter
  {
    /0001 { /type "allow" /glob "*" }
  }

  /cache
  {
    /docroot "/path/to/apache2.2/htdocs"
    /rules
    {
      /0000 { /glob "*" /type "allow" }
    }
    /statfileslevel "3"
    /allowAuthorized "0"
  }

  /statfile "/tmp/dispatcher-localhost.stat"
}
</code></pre>
<blockquote>
<p>The <code>/filter</code> section above is intentionally permissive (<code>"allow" "*"</code>) for <strong>local testing only</strong>. Never use an open filter like this in a staging or production environment — always define explicit allow/deny rules based on Adobe's recommended Dispatcher security checklist.</p>
</blockquote>
<h2>Step 8: Set Up the Document Root</h2>
<p>Make sure the <code>docroot</code> path in your farm config exists and matches Apache's <code>DocumentRoot</code> directive in <code>httpd.conf</code>:</p>
<pre><code class="language-apache">DocumentRoot "/path/to/apache2.2/htdocs"
&lt;Directory "/path/to/apache2.2/htdocs"&gt;
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
&lt;/Directory&gt;
</code></pre>
<h2>Step 9: Set Apache to Listen on the Right Port</h2>
<p>By default, Apache listens on port 80. For local testing, this is usually fine, but if port 80 is taken, change it:</p>
<pre><code class="language-apache">Listen 8080
</code></pre>
<h2>Step 10: Restart Apache and Test</h2>
<p>Restart Apache:</p>
<pre><code class="language-bash"># Linux/macOS
sudo apachectl restart

# Windows (from Apache bin directory)
httpd.exe -k restart
</code></pre>
<p>Then hit your site through Apache (not directly through AEM's port):</p>
<pre><code class="language-plaintext">http://localhost:8080/
</code></pre>
<p>If everything's wired correctly, you should see your AEM publish instance's content served through Apache/Dispatcher.</p>
<h2>Step 11: Verify Caching Is Working</h2>
<p>Check the <code>docroot</code> folder you configured — after a successful request, Dispatcher should write a static cached copy of the page (e.g., <code>.html</code> file) into that directory. You can also tail the Dispatcher log to confirm requests are being processed:</p>
<pre><code class="language-bash">tail -f /path/to/apache2.2/logs/dispatcher.log
</code></pre>
<h2>Common Issues &amp; Fixes</h2>
<ul>
<li><p><strong>Apache fails to start after adding the module</strong> — Usually an architecture mismatch (32-bit module on a 64-bit Apache, or vice versa). Re-check <code>httpd -V</code>.</p>
</li>
<li><p><strong>Blank page / 404 through Apache</strong> — Double-check the <code>/renders</code> hostname and port match your actual AEM publish instance, and that AEM is running.</p>
</li>
<li><p><strong>Nothing gets cached</strong> — Verify the <code>docroot</code> path is writable by the user running Apache, and that your <code>/cache/rules</code> aren't blocking the request path.</p>
</li>
<li><p><strong>Changes to dispatcher.any not taking effect</strong> — Dispatcher config is only re-read on Apache restart, not on every request. Always restart Apache after editing <code>.any</code> files.</p>
</li>
</ul>
<h2>Wrapping Up</h2>
<p>This setup mirrors (at a smaller scale) how AEM Dispatcher works in real deployments — caching pages, applying security filters, and routing requests to the right render instance. Getting comfortable with this locally makes it much easier to debug dispatcher-related issues later in staging or production, where you often don't have direct server access.</p>
<p>In a future post, we'll look at moving this same setup to <strong>Apache 2.4</strong> and comparing the syntax differences in <code>httpd.conf</code>, along with a deeper dive into Dispatcher security rules (the <code>/filter</code> section) that you should never skip in production.</p>
]]></content:encoded></item></channel></rss>