Skip to main content

Extending the AEM Connector

The AEM connector provides a powerful extension system that lets you customize how content is extracted, transformed, and indexed from Adobe Experience Manager. Extensions are Java classes that implement interfaces from the aem-commons library — published on Maven Central.


Maven Dependency

To create custom AEM extensions, add aem-commons to your project:

<!-- Source: https://mvnrepository.com/artifact/com.viglet.dumont/aem-commons -->
<dependency>
<groupId>com.viglet.dumont</groupId>
<artifactId>aem-commons</artifactId>
<version>2026.1.19</version>
<scope>compile</scope>
</dependency>

Extension Interfaces

The AEM connector provides four extension interfaces:

InterfacePurposeConfig field
DumAemExtAttributeInterfaceCustom logic for extracting or transforming individual field valuesattributes[].className or sourceAttrs[].className
DumAemExtContentInterfaceExtract additional content from AEM pages (e.g., .model.json)models[].className
DumAemExtDeltaDateInterfaceCustom delta date resolution for incremental indexingsources[].deltaClass
DumAemExtUrlAttributeInterfaceSpecialized URL handling with ID extraction (extends DumAemExtAttributeInterface)attributes[].className

DumAemExtAttributeInterface

The most commonly used extension. Implement this to transform or extract individual attribute values with custom logic.

import com.viglet.dumont.connector.aem.commons.ext.DumAemExtAttributeInterface;
import com.viglet.dumont.connector.aem.commons.DumAemObject;
import com.viglet.dumont.connector.aem.commons.context.DumAemConfiguration;
import com.viglet.dumont.connector.aem.commons.mappers.*;
import com.viglet.turing.client.sn.TurMultiValue;

public class MyCustomAttribute implements DumAemExtAttributeInterface {

@Override
public TurMultiValue consume(
DumAemTargetAttr dumAemTargetAttr,
DumAemSourceAttr dumAemSourceAttr,
DumAemObject aemObject,
DumAemConfiguration dumAemConfiguration
) {
// Example: extract a custom property and transform it
String rawValue = aemObject.getAttributes()
.get("myProperty").toString();
return TurMultiValue.singleItem(rawValue.toUpperCase());
}
}

Parameters:

ParameterDescription
DumAemTargetAttrTarget search field being populated (name, type)
DumAemSourceAttrSource AEM property definition (name, className)
DumAemObjectAEM content node: path, title, template, jcrNode, jcrContentNode, lastModified, attributes
DumAemConfigurationConnection config: url, username, rootPath, authorSNSite, publishSNSite, providerName

Built-in implementations:

ClassWhat it does
DumAemExtContentIdReturns the AEM page path as the document ID
DumAemExtContentUrlBuilds the full URL from the page path and URL prefix config
DumAemExtContentTagsFetches tags from the /jcr:content.tags.json endpoint
DumAemExtCreationDateReturns the jcr:created date
DumAemExtModificationDateReturns the cq:lastModified or jcr:lastModified date
DumAemExtPublicationDateReturns the last replication date
DumAemExtHtml2TextConverts HTML content to plain text
DumAemExtPageComponentsExtracts text from responsive grid components
DumAemExtTypeNameReturns the content type name
DumAemExtSourceAppsReturns the provider name from configuration
DumAemExtSiteNameReturns the site name

DumAemExtContentInterface

Implement this to fetch additional content from AEM — for example, calling the .model.json Sling Model exporter to get structured data.

import com.viglet.dumont.connector.aem.commons.ext.DumAemExtContentInterface;
import com.viglet.dumont.connector.aem.commons.bean.DumAemTargetAttrValueMap;

public class MyModelJsonExtractor implements DumAemExtContentInterface {

@Override
public DumAemTargetAttrValueMap consume(
DumAemObject aemObject,
DumAemConfiguration dumAemConfiguration
) {
DumAemTargetAttrValueMap result = new DumAemTargetAttrValueMap();

// Fetch the .model.json endpoint
String url = dumAemConfiguration.getUrl()
+ aemObject.getPath() + ".model.json";
// ... HTTP call to get JSON ...

result.addWithSingleValue("fragmentPath",
"/content/dam/fragment", false);
return result;
}
}

Referenced in the models[].className field of the configuration JSON.


DumAemExtDeltaDateInterface

Customize how the connector determines the "last modified" date for incremental indexing.

import com.viglet.dumont.connector.aem.commons.ext.DumAemExtDeltaDateInterface;

public class MyDeltaDate implements DumAemExtDeltaDateInterface {

@Override
public Date consume(
DumAemObject aemObject,
DumAemConfiguration dumAemConfiguration
) {
return Optional.ofNullable(aemObject.getLastModified())
.map(Calendar::getTime)
.orElse(null);
}
}

Referenced in the sources[].deltaClass field of the configuration JSON.


AEM Configuration JSON

The AEM connector is configured via a JSON file that defines sources, attributes, locale mappings, and content models. This file is placed in an export/ directory and imported at startup.

Full Example (WKND Site)

{
"sources": [
{
"name": "WKND",
"defaultLocale": "en_US",
"localeClass": "com.viglet.dumont.connector.aem.commons.ext.DumAemExtLocale",
"deltaClass": "com.example.MyDeltaDate",
"endpoint": "http://localhost:4502",
"username": "admin",
"password": "admin",
"oncePattern": "^/content/wknd/us/en/faqs",
"rootPath": "/content/wknd",
"contentType": "cq:Page",
"author": true,
"publish": true,
"authorSNSite": "wknd-author",
"publishSNSite": "wknd-publish",
"authorURLPrefix": "http://localhost:4502",
"publishURLPrefix": "https://wknd.site",
"localePaths": [
{ "locale": "en_US", "path": "/content/wknd/us/en" },
{ "locale": "es", "path": "/content/wknd/es/es" }
],
"attributes": [
{
"name": "id", "type": "STRING", "mandatory": true,
"className": "com.viglet.dumont.connector.aem.commons.ext.DumAemExtContentId"
},
{
"name": "title", "type": "TEXT", "mandatory": true,
"facetName": { "default": "Titles", "pt_BR": "Títulos" }
},
{
"name": "tags", "type": "STRING", "multiValued": true,
"facet": true, "facetName": { "default": "Tags" },
"className": "com.viglet.dumont.connector.aem.commons.ext.DumAemExtContentTags"
},
{
"name": "url", "type": "STRING", "mandatory": true,
"className": "com.viglet.dumont.connector.aem.commons.ext.DumAemExtContentUrl"
}
],
"models": [
{
"type": "cq:Page",
"className": "com.example.MyModelJsonExtractor",
"targetAttrs": [
{ "name": "title", "sourceAttrs": [{ "name": "jcr:title" }] },
{ "name": "tags", "sourceAttrs": [{ "name": "cq:tags" }] },
{
"name": "text",
"sourceAttrs": [{
"className": "com.viglet.dumont.connector.aem.commons.ext.DumAemExtPageComponents"
}]
}
]
}
]
}
]
}

Source Fields

FieldTypeDescription
namestringSource identifier (displayed in the admin console)
endpointstringAEM instance URL (e.g., http://localhost:4502)
username / passwordstringAEM authentication credentials
rootPathstringContent tree root to crawl (e.g., /content/wknd)
contentTypestringJCR node type to index (e.g., cq:Page)
defaultLocalestringFallback locale code (e.g., en_US)
localeClassstringClass for locale resolution
deltaClassstringClass implementing DumAemExtDeltaDateInterface
oncePatternstringRegex — matching paths are indexed only once (never re-indexed)
author / publishbooleanEnable indexing from author/publish environments
authorSNSite / publishSNSitestringTuring ES SN Site names for each environment
authorURLPrefix / publishURLPrefixstringPublic URL prefixes for documents

Locale Paths

"localePaths": [
{ "locale": "en_US", "path": "/content/wknd/us/en" },
{ "locale": "es", "path": "/content/wknd/es/es" }
]

Content found under each path is tagged with the corresponding locale.

Attribute Fields

FieldTypeDescription
namestringField name in the search index
typestringSTRING, TEXT, or DATE
mandatorybooleanWhether this field is required
multiValuedbooleanWhether this field holds multiple values
descriptionstringHuman-readable description
facetbooleanExpose as a facet filter in search results
facetNameobjectLocalized labels: { "default": "Tags", "pt_BR": "Etiquetas" }
classNamestringClass implementing DumAemExtAttributeInterface — extracts the value instead of reading from JCR

Model Fields

FieldTypeDescription
typestringJCR node type this model applies to
classNamestringClass implementing DumAemExtContentInterface
targetAttrs[].namestringTarget field (must match attributes[])
targetAttrs[].sourceAttrs[].namestringJCR property to read (e.g., jcr:title)
targetAttrs[].sourceAttrs[].classNamestringClass implementing DumAemExtAttributeInterface for custom extraction

Creating a Custom AEM Extension

Step 1 — Create a Maven project

<project>
<groupId>com.example</groupId>
<artifactId>my-aem-extensions</artifactId>
<version>1.0.0</version>

<dependencies>
<dependency>
<groupId>com.viglet.dumont</groupId>
<artifactId>aem-commons</artifactId>
<version>2026.1.19</version>
</dependency>
</dependencies>
</project>

Step 2 — Implement your extension

package com.example.ext;

import com.viglet.dumont.connector.aem.commons.ext.DumAemExtAttributeInterface;
import com.viglet.turing.client.sn.TurMultiValue;

public class MyBreadcrumb implements DumAemExtAttributeInterface {
@Override
public TurMultiValue consume(DumAemTargetAttr target,
DumAemSourceAttr source, DumAemObject aemObject,
DumAemConfiguration config) {
String path = aemObject.getPath()
.replace(config.getRootPath(), "");
return TurMultiValue.singleItem(
String.join(" > ", path.split("/")));
}
}

Step 3 — Build and deploy

mvn clean package
cp target/my-aem-extensions-1.0.0.jar /appl/viglet/dumont/aem/libs/

The libs/ directory must contain both aem-plugin.jar and your extension JAR.

Step 4 — Reference in the JSON

{
"name": "breadcrumb",
"type": "STRING",
"className": "com.example.ext.MyBreadcrumb"
}

How classes are loaded

Extension classes are loaded via DumCustomClassCache using Class.forName(). Requirements:

  • Public no-argument constructor
  • On the classpath (via libs/ and -Dloader.path)
  • Thread-safe (one instance is shared across all calls)

PageDescription
AEM ConnectorAEM connector features, configuration, and locale mapping
Installation GuideHow to deploy plugins with -Dloader.path
Developer GuideProject structure, build, and contribution guide