> 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.

# JCR & Sling

## What is the JCR

JCR is a hierarchy, tree-structured content repository.

```mermaid
graph TD
    %% Main nodes (Level 1)
    A["/content"]
    B["/apps"]
    C["/conf"]
    D["/libs"]
    E["/var"]
    F["/etc"]
    G["/home"]
    H["/content/mysite"]
    I["/apps/mysite"]
    
    %% Child nodes (Level 2 & 3)
    J["/content/mysite/en"]
    K["/apps/mysite/components"]
    L["/apps/mysite/clientlibs"]
    M["/content/mysite/en (page)"]
    N["/content/mysite/en/about"]

    %% Root connections
    Root["/ (Root)"] --> A
    Root --> B
    Root --> C
    Root --> D
    Root --> E
    Root --> F
    Root --> G

    %% /content connections
    A --> H
    H --> J
    J --> M
    J --> N

    %% /apps connections
    B --> I
    I --> K
    I --> L
```

| Path       | Contents                                                         |
| ---------- | ---------------------------------------------------------------- |
| `/apps`    | Your project's code - components, templates, clientlibs, configs |
| `/libs`    | AEM's built-in code - Core Components, platform features         |
| `/content` | Authored content - pages, assets                                 |
| `/conf`    | Configuration - editable templates, Cloud Configurations         |
| `/var`     | Runtime data - workflow instances, audit logs                    |
| `/etc`     | Legacy config area (some items still here)                       |
| `/home`    | Users 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 type             | Use                                                             |
| --------------------- | --------------------------------------------------------------- |
| `cq:Page`             | A page in the Sites console                                     |
| `cq:PageContent`      | The content node of a page (`jcr:content`)                      |
| `nt:unstructured`     | Generic node - no schema constraints. Most component data nodes |
| `nt:folder`           | A basic folder                                                  |
| `sling:Folder`        | A Sling-aware folder                                            |
| `sling:OrderedFolder` | Folder with ordered children                                    |
| `cq:Component`        | A component definition node                                     |
| `cq:Template`         | A template definition node                                      |
| `dam:Asset`           | A digital asset (image, document)                               |

Nodes can also have **mixin types** to add extra capabilities.

| Mixin                    | Adds                    |
| ------------------------ | ----------------------- |
| `mix:versionable`        | Version history         |
| `mix:lockable`           | Locking support         |
| `cq:LiveRelationship`    | Live Copy support (MSM) |
| `rep:AccessControllable` | ACL 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

```mermaid
flowchart LR
    A["HTTP Request"] --> B["Resource Resolution"]
    B --> C["Script/Servlet Selection"]
    C --> D["Rendering (HTL/Servlet)"]
    D --> E["HTTP Response"]
```

**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]_

| Component         | Example                    | Purpose                                          |
| :---------------- | :------------------------- | :----------------------------------------------- |
| **Resource path** | `/content/mysite/en/about` | Maps to a JCR node                               |
| **Selectors**     | `article`                  | Alternate rendering (e.g., `about.article.html`) |
| **Extension**     | `html`                     | Output format                                    |
| **Suffix**        | `/suffix`                  | Additional path information                      |
| **Query string**  | `key=value`                | Parameters                                       |

**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.

```mermaid
flowchart TD
    A["HTTP Request<br/>/content/mysite/en/about.html"] --> B{"Node exists at<br/>/content/mysite/en/about?"}
    
    B -->|Yes| C["Resource:<br/>/content/mysite/en/about"]
    B -->|No| D["404 Not Found<br/>(Error handling)"]
    
    C --> E["Read sling:resourceType<br/>mysite/components/page"]
    E --> F["Find script at<br/>/apps/mysite/components/page/page.html"]
    F --> G["Render HTML template<br/>(HTL/Script)"]
    G --> H["HTTP Response<br/>Returns HTML"]
    
    D --> I["HTTP 404 Response"]
```

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
```

- [Sling Resource Merger](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/developing/sling-resource-merger)

### Script selection rules

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

| Request            | Script looked up                                    |
| ------------------ | --------------------------------------------------- |
| `GET.html`         | `text.html` or `html.html`                          |
| `GET.json`         | `text.json` (or Sling Default GET Servlet for JSON) |
| `GET.article.html` | `text.article.html` then `text.html`                |
| `POST.html`        | `text.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.

```java
// 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:

| Interface            | Purpose                                                 |
| -------------------- | ------------------------------------------------------- |
| `Resource`           | Represents a JCR node (or virtual resource)             |
| `ResourceResolver`   | Resolves paths to resources, creates/modifies resources |
| `ValueMap`           | Read properties from a resource (like a type-safe Map)  |
| `ModifiableValueMap` | Write properties to a resource                          |

## Adaptable pattern

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

```java
// 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)**

| Path                       | Contents                     |
| -------------------------- | ---------------------------- |
| `/apps/mysite/components/` | Your component definitions   |
| `/apps/mysite/clientlibs/` | Your CSS/JS client libraries |
| `/apps/mysite/i18n/`       | Your i18n dictionaries       |

**Content paths (mutable)**

| Path                                    | Contents                                      |
| --------------------------------------- | --------------------------------------------- |
| `/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**

| Path            | Contents                         |
| --------------- | -------------------------------- |
| `/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:

```java
@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:

```sql
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`.

:::warning\[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.
:::
