For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt, and this page is available as Markdown at https://nhanbe.id.vn/aem/foundation/jcr-and-sling.md.

JCR is a hierarchy, tree-structured content repository. JCR has nodes and properties. Nodes are like folders. They have a name, type, and contain

JCR & Sling

What is the JCR

JCR is a hierarchy, tree-structured content repository.

PathContents
/appsYour project's code - components, templates, clientlibs, configs
/libsAEM's built-in code - Core Components, platform features
/contentAuthored content - pages, assets
/confConfiguration - editable templates, Cloud Configurations
/varRuntime data - workflow instances, audit logs
/etcLegacy config area (some items still here)
/homeUsers and groups

Nodes and properties

JCR has nodes and properties.

  • Nodes are like folders. They have a name, type, and contain child nodes and properties.
  • Properties are key-value pairs attached to a node. Values can be strings, numbers, dates, booleans, or binary data.
/content/mysite/en/about
├── jcr:primaryType = "cq:Page"
└── jcr:content
    ├── jcr:primaryType = "cq:PageContent"
    ├── jcr:title = "About Us"
    ├── sling:resourceType = "mysite/components/page"
    ├── cq:template = "/conf/mysite/settings/wcm/templates/page"
    └── root
        ├── jcr:primaryType = "nt:unstructured"
        ├── sling:resourceType = "mysite/components/container"
        └── text
            ├── jcr:primaryType = "nt:unstructured"
            ├── sling:resourceType = "mysite/components/text"
            └── text = "<p>Welcome to our company.</p>"
  • Pages: cq:Page nodes with a jcr:content child.
  • Page content lives under jcr:content - this is where properties like jcr:title are stored.
  • Components on a page are child nodes under the jcr:content tree.
  • sling:resourceType tells Sling which component will render this node.

Node types

Node typeUse
cq:PageA page in the Sites console
cq:PageContentThe content node of a page (jcr:content)
nt:unstructuredGeneric node - no schema constraints. Most component data nodes
nt:folderA basic folder
sling:FolderA Sling-aware folder
sling:OrderedFolderFolder with ordered children
cq:ComponentA component definition node
cq:TemplateA template definition node
dam:AssetA digital asset (image, document)

Nodes can also have mixin types to add extra capabilities.

MixinAdds
mix:versionableVersion history
mix:lockableLocking support
cq:LiveRelationshipLive Copy support (MSM)
rep:AccessControllableACL support

CRXDE basics

Edit content and explore items:

  • /content/mysite/en - your site's English root page
  • /apps/mysite/components - your project's component definitions
  • /libs/core/wcm/components - AEM Core Components source
  • /conf/mysite/settings/wcm/templates - your editable templates

Apache Sling - the web framework

Sling is a web framework that sits between HTTP and JCR. Its main job: resolve URLs to JCR structures, then render them.

Sling request processing pipeline

When a request like GET /content/mysite/en/about.html arrives:

  1. Resource resolution - Sling parses the URL and finds the JCR node at /content/mysite/en/about.
  2. Script selection - Sling reads the sling:resourceType property (e.g., mysite/components/page) and finds the corresponding rendering script.
  3. Rendering - The HTL template (or servlet) renders the content.
  4. Response - The rendered HTML is returned.

URL decomposition

Sling splits URLs into parts:

[Image unavailable: image-2.png]

ComponentExamplePurpose
Resource path/content/mysite/en/aboutMaps to a JCR node
SelectorsarticleAlternate rendering (e.g., about.article.html)
ExtensionhtmlOutput format
Suffix/suffixAdditional path information
Query stringkey=valueParameters

Selectors are powerful - they let you render the same content in multiple ways without creating new endpoints:

  • /about.html - default rendering
  • /about.article.html - article rendering
  • /about.json - JSON output
  • /about.xml - XML output

Resource Resolution in detail

Resource resolution is how Sling finds the correct JCR node for a URL.

The sling:resourceType property on a node points to a component definition. Sling searches for rendering scripts in the following order:

  • /apps/<resourceType>/<scriptName>.html - your project's override
  • /libs/<resourceType>/<scriptName>.html - AEM's default

This is the overlay mechanism - you can override any AEM component by placing your version in /apps.

A component can also declare a sling:resourceSuperType property pointing to a parent component. When Sling cannot find a script in the current component, it walks up the super-type chain and uses the parent component's script.

This is how proxy components work - a component in your project inherits all rendering from a Core Component and only overrides what is needed:

/apps/mysite/components/text
    sling:resourceSuperType = "core/wcm/components/text/v2/text"
    → Sling will use the Core Component's script unless you provide your own

Script selection rules

For a resource with sling:resourceType = "mysite/components/text", Sling looks for scripts in /apps/mysite/components/text/:

RequestScript looked up
GET.htmltext.html or html.html
GET.jsontext.json (or Sling Default GET Servlet for JSON)
GET.article.htmltext.article.html then text.html
POST.htmltext.POST.html

Script naming conventions: <component>.<selector>.<extension>.html or the shorthand <extension>.html.

For a resource with sling:resourceType = "mysite/components/text", Sling's Script Resolution / Script Selection mechanism is actually more complex and depends on multiple request factors, not just the extension.


1. Factors Sling uses to select a script

Sling determines the script based on:

  • Resource Type (mysite/components/text)
  • HTTP Method (GET, POST, PUT, DELETE…)
  • Selectors (e.g., .article, .print)
  • Extension (.html, .json, .xml…)
  • Suffix (does not directly affect script selection)
  • Resource Super Type chain
  • Search Path (/apps → /libs)

2. Script lookup order (highest → lowest priority)

Sling searches for scripts in a specific → more general order:

Example request:
GET /content/page.article.print.html
Lookup order:
/apps/mysite/components/text/text.article.print.html
/apps/mysite/components/text/article.print.html

/apps/mysite/components/text/text.article.html
/apps/mysite/components/text/article.html

/apps/mysite/components/text/text.print.html
/apps/mysite/components/text/print.html

/apps/mysite/components/text/text.html
/apps/mysite/components/text/html.html

If not found, fall through to:

/libs/mysite/components/text/…

If still not found, continue following sling:resourceSuperType.


3. Full naming rules

Full format:
<resourceType>.<selector>.<extension>.<method>
Common formats:
<component>.<selector>.<extension>.html
<selector>.<extension>.html
<extension>.html

4. Specific request mappings

4.1 GET.html
GET /page.html

Lookup:

text.html
html.html

4.2 GET with selectors
GET /page.article.html

Lookup:

text.article.html
article.html
text.html

4.3 Multiple selectors
GET /page.a.b.html

Lookup:

text.a.b.html
a.b.html
text.a.html
a.html
text.b.html
b.html
text.html

4.4 GET.json
GET /page.json

Lookup:

text.json
json.json

If not found, fallback to:

  • Sling Default GET Servlet (renders JSON from JCR)

4.5 POST
POST /page.html

Lookup:

text.POST.html
POST.html

If not found, fallback to:

  • Sling POST Servlet

5. Super Type chain

If not found in the current component:

mysite/components/text
   ↓
core/wcm/components/text/v2/text

Sling continues looking up using the same rules in the parent component.


6. Search Path (overlay)

Order:

/apps → /libs

→ /apps overrides /libs


7. Key principles summary

  • Sling always prioritizes:

    1. More specific (more selectors)
    2. Correct HTTP method
    3. Correct extension
    4. Current component first
    5. Then super type
    6. Finally /libs
  • Scripts can be written as:

    • Component-specific (text.html)
    • Generic (html.html)
  • Selector resolution is a progressive fallback.


8. Practical example

Request:

GET /content/page.article.html

Sling tries:

  1. /apps/mysite/components/text/text.article.html
  2. /apps/mysite/components/text/article.html
  3. /apps/mysite/components/text/text.html
  4. /libs/…
  5. resourceSuperType
  6. Default servlet

Resources and the Resource API

In Sling, everything is a Resource. A Resource is a Java abstraction of a JCR node.

// Get the ResourceResolver
ResourceResolver resolver = request.getResourceResolver();

// Resolve a resource by path
Resource page = resolver.getResource("/content/mysite/en/about");

// Read properties
ValueMap properties = page.getChild("jcr:content").getValueMap();
String title = properties.get("jcr:title", "Untitled");

// Iterate children
for (Resource child : page.getChildren()) {
    String name = child.getName();
    String type = child.getResourceType();
}

Key interfaces:

InterfacePurpose
ResourceRepresents a JCR node (or virtual resource)
ResourceResolverResolves paths to resources, creates/modifies resources
ValueMapRead properties from a resource (like a type-safe Map)
ModifiableValueMapWrite properties to a resource

Adaptable pattern

Sling extensively uses the Adaptable design pattern - you convert (adapt) one object to another:

// Adapt a Resource to a Page
Page page = resource.adaptTo(Page.class);

// Adapt a Resource to a Sling Model
MyModel model = resource.adaptTo(MyModel.class);

// Adapt a Resource to a ValueMap
ValueMap props = resource.adaptTo(ValueMap.class);

// Adapt a Resource to a Node (JCR API)
Node node = resource.adaptTo(Node.class);

This pattern is central to AEM development. Sling Models (chapter 7) are the primary way to adapt resources into custom Java objects.

Content paths and conventions

AEM has strong conventions for where content is stored:

Code paths (immutable in AEMaaCS)

PathContents
/apps/mysite/components/Your component definitions
/apps/mysite/clientlibs/Your CSS/JS client libraries
/apps/mysite/i18n/Your i18n dictionaries

Content paths (mutable)

PathContents
/content/mysite/Site pages
/content/dam/mysite/Digital assets (images, PDFs)
/content/experience-fragments/mysite/Experience Fragments
/conf/mysite/Site configuration (templates, Cloud Configs)

System paths

PathContents
/libs/AEM built-in code - never modify
/var/Runtime data (workflow, audit)
/home/users/User accounts
/home/groups/Groups

Rule: Never modify /libs. Always overlay or extend in /apps. In AEMaaCS, /libs is read-only.

Querying the JCR

Beyond traversing the tree, you can query it. AEM supports two query languages:

QueryBuilder

AEM's query API - used in Java code and HTTP:

http://localhost:4502/bin/querybuilder.json?
  path=/content/mysite
  &type=cq:Page
  &property=jcr:content/jcr:title
  &property.operation=like
  &property.value=%25about%25
  &p.limit=10

In Java (e.g., inside a Sling Model), use the QueryBuilder service:

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class PageSearchModel {

    @OSGiService
    private QueryBuilder queryBuilder;

    @Self
    private Resource resource;

    public List<String> findPageTitles() {
        Map<String, String> params = new HashMap<>();
        params.put("path", "/content/mysite");
        params.put("type", "cq:Page");
        params.put("property", "jcr:content/jcr:title");
        params.put("property.operation", "like");
        params.put("property.value", "%about%");
        params.put("p.limit", "10");

        ResourceResolver resolver = resource.getResourceResolver();
        Session session = resolver.adaptTo(Session.class);
        Query query = queryBuilder.createQuery(PredicateGroup.create(params), session);
        SearchResult result = query.getResult();

        List<String> titles = new ArrayList<>();
        for (Hit hit : result.getHits()) {
            try {
                titles.add(hit.getTitle());
            } catch (RepositoryException e) {
                // handle exception
            }
        }
        return titles;
    }
}

JCR-SQL2

SQL-like syntax for JCR queries:

SELECT *
FROM [cq:Page] AS page
INNER JOIN [nt:unstructured] AS content ON ISCHILDNODE(content, page)
WHERE ISDESCENDANTNODE(page, '/content/mysite')
  AND content.[jcr:title] LIKE '%about%'

Note: The syntax page.[jcr:content/jcr:title] is not valid in JCR-SQL2. To query properties on child nodes, you must use INNER JOIN with ISCHILDNODE() as shown above.

You can run queries using the query tool in CRXDE Lite (bottom panel) or the QueryBuilder Debugger at http://localhost:4502/libs/cq/search/content/querydebug.html.

[Performance warning]

Queries without proper indexes will be slow and can block the repository. Always test query performance and create Oak indexes for production queries. See the JCR references for details.